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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +0 -12
- data/CHANGELOG.md +17 -0
- data/CONTRIBUTING.md +42 -7
- data/Gemfile +1 -0
- data/README.md +108 -12
- 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/dsl.rb +3 -1
- data/lib/with_model/model.rb +14 -12
- data/lib/with_model/null_table.rb +49 -19
- data/lib/with_model/rspec.rb +10 -0
- data/lib/with_model/table.rb +8 -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/readme_spec.rb +20 -0
- data/spec/runner_entrypoints_spec.rb +153 -0
- data/spec/table_false_spec.rb +169 -4
- data/spec/table_spec.rb +37 -0
- data/test/own_table_name_test.rb +81 -0
- data/test_unit/lifecycle_test.rb +186 -0
- data/test_unit/own_table_name_test.rb +81 -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 +22 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 82365b7ef365626c49c238c05258d85603f6d9cf18c080c5ef39329c1ecf3346
|
|
4
|
+
data.tar.gz: d1cf7a5a2f060ab68b1daf20a0495de5a09b706b272387810c3c4acce6e42470
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6c4d55dda6fe90872b8a31799207acfe4a910734f20e26cd870103d4ddee2dac6f7f756474f2982e944337ec06502b507622a95c89d400df64aae763554b04c7
|
|
7
|
+
data.tar.gz: f3fc56af09b5da30a0bde842ff0cc66d448089a638bce6203485ce9f48fe35014b21c05cb1d1f06fc179f49077c58f365837e2f5ab15534d0781a0f4f297c4d8
|
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,5 +1,22 @@
|
|
|
1
1
|
### Unreleased
|
|
2
2
|
|
|
3
|
+
### 2.5.0
|
|
4
|
+
|
|
5
|
+
- Allow `table(false)` for an abstract model, and for a model whose superclass
|
|
6
|
+
is `ActiveRecord::Base` or abstract when the model has an existing table: the
|
|
7
|
+
one its model block names with `self.table_name`, or else the one Active
|
|
8
|
+
Record names after the model. Its rows are left for the suite to clean up.
|
|
9
|
+
|
|
10
|
+
### 2.4.0
|
|
11
|
+
|
|
12
|
+
- Add Test::Unit support, including lifecycle integration for dynamic models and
|
|
13
|
+
tables.
|
|
14
|
+
- Add `with_model/rspec`, `with_model/minitest`, and `with_model/test_unit`
|
|
15
|
+
convenience entrypoints.
|
|
16
|
+
- Add runner integration guides for RSpec, Minitest, and Test::Unit.
|
|
17
|
+
- Skip temporary-table teardown when the table has already disappeared.
|
|
18
|
+
- Require Active Record 8.0 or later.
|
|
19
|
+
|
|
3
20
|
### 2.3.1
|
|
4
21
|
|
|
5
22
|
- Honor Active Record destruction callbacks and dependent associations when
|
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`:
|
|
@@ -75,9 +92,27 @@ bundle exec standardrb --fix
|
|
|
75
92
|
|
|
76
93
|
## Changes and releases
|
|
77
94
|
|
|
78
|
-
Please add
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
95
|
+
Please add an entry to `CHANGELOG.md` under `### Unreleased` for each change
|
|
96
|
+
someone using the gem would notice. Leave `lib/with_model/version.rb` alone:
|
|
97
|
+
version bumps and releases are cut separately by a maintainer, so a bump in a
|
|
98
|
+
pull request only creates a conflict.
|
|
99
|
+
|
|
100
|
+
### Changelog format
|
|
101
|
+
|
|
102
|
+
- Each release has a `### X.Y.Z` heading, newest first, with unreleased changes
|
|
103
|
+
under `### Unreleased` at the top. Leave a blank line after each heading.
|
|
104
|
+
- Write one bullet per change, from the point of view of someone using the gem:
|
|
105
|
+
what they can now do or what behaves differently, not how the code changed.
|
|
106
|
+
- Start with a verb in the imperative mood (Add, Allow, Fix, Deprecate, Remove,
|
|
107
|
+
Require), and write a full sentence ending in a period.
|
|
108
|
+
- Put code, such as method names, options, and constants, in backticks.
|
|
109
|
+
- Wrap lines at 80 columns, indenting continuation lines by two spaces.
|
|
110
|
+
- List dependency changes last, Active Record before Ruby, with added support
|
|
111
|
+
before a raised minimum: "Add support for Ruby 4.0." then "Require Ruby 3.3
|
|
112
|
+
or later."
|
|
113
|
+
- Don't cite issue or pull request numbers. The pull request links the issue.
|
|
114
|
+
- Credit contributors from outside the maintainers by name, in parentheses at
|
|
115
|
+
the end of the entry, as in "Create table in same database as `superclass`
|
|
116
|
+
(John Doe)."
|
|
82
117
|
|
|
83
118
|
Last, but not least, have fun.
|
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
|
+
```
|
|
59
|
+
|
|
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:
|
|
37
63
|
|
|
38
|
-
|
|
39
|
-
|
|
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.
|
|
@@ -206,8 +237,9 @@ end
|
|
|
206
237
|
A `superclass:` that cannot be used raises `WithModel::InvalidSuperclass`: one
|
|
207
238
|
that is not an Active Record class, one with no table of its own
|
|
208
239
|
(`ActiveRecord::Base`, or an abstract class such as a Rails app's
|
|
209
|
-
`ApplicationRecord`)
|
|
210
|
-
|
|
240
|
+
`ApplicationRecord`) when the model has no existing table of its own either (see
|
|
241
|
+
[Existing tables](#existing-tables)), or a concrete one whose table has no
|
|
242
|
+
inheritance column to tell a subclass's rows apart. A name that resolves to nothing raises
|
|
211
243
|
`WithModel::MissingSuperclass`, a kind of `InvalidSuperclass`, quoting the
|
|
212
244
|
constant Ruby could not find — for a namespaced name, that is the segment which
|
|
213
245
|
is actually missing. Both are `ArgumentError`s.
|
|
@@ -218,6 +250,64 @@ enough on its own. Rows the model wrote are deleted when it goes away, since the
|
|
|
218
250
|
name a class that is about to stop existing and would make the superclass
|
|
219
251
|
unloadable.
|
|
220
252
|
|
|
253
|
+
### Existing tables
|
|
254
|
+
|
|
255
|
+
`table(false)` also suits a model that maps to a table the suite already has,
|
|
256
|
+
such as a legacy table or one that several models share. Set `self.table_name`
|
|
257
|
+
in the model block, or leave it out to use the table Active Record names after
|
|
258
|
+
the model (`phoenixes` for `Phoenix`):
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
describe "with_model supports existing tables" do
|
|
262
|
+
with_table :legendary_creatures do |t|
|
|
263
|
+
t.string "name"
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
with_model :Phoenix do
|
|
267
|
+
table(false)
|
|
268
|
+
|
|
269
|
+
model do
|
|
270
|
+
self.table_name = "legendary_creatures"
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
it "uses the table it names" do
|
|
275
|
+
Phoenix.create!(name: "Fawkes")
|
|
276
|
+
|
|
277
|
+
expect(Phoenix.table_name).to eq "legendary_creatures"
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The table must exist before the model is set up, so declare `with_table` (or
|
|
283
|
+
whatever creates it) first. It belongs to the suite, not the model, so its rows
|
|
284
|
+
are left in place when the model goes away. Clean them up as you would the rest of that table, with
|
|
285
|
+
transactional tests or otherwise.
|
|
286
|
+
|
|
287
|
+
The superclass can be abstract, as a Rails app's `ApplicationRecord` is. An
|
|
288
|
+
abstract model needs no table, so declare it with `table(false)` as well:
|
|
289
|
+
|
|
290
|
+
```ruby
|
|
291
|
+
with_model :LegendaryCreature do
|
|
292
|
+
table(false)
|
|
293
|
+
|
|
294
|
+
model do
|
|
295
|
+
self.abstract_class = true
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
with_model :Phoenix, superclass: :LegendaryCreature do
|
|
300
|
+
table(false)
|
|
301
|
+
|
|
302
|
+
model do
|
|
303
|
+
self.table_name = "legendary_creatures"
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Active Record never applies single table inheritance below an abstract class, so
|
|
309
|
+
an abstract superclass's table, if it has one, needs no inheritance column.
|
|
310
|
+
|
|
221
311
|
## Foreign keys
|
|
222
312
|
|
|
223
313
|
`foreign_key: true` asks Active Record to infer the table a foreign key points
|
|
@@ -252,7 +342,13 @@ the order they are declared, and destroyed in the reverse order.
|
|
|
252
342
|
|
|
253
343
|
## Requirements
|
|
254
344
|
|
|
255
|
-
See the [gemspec metadata](https://rubygems.org/gems/with_model) for
|
|
345
|
+
See the [gemspec metadata](https://rubygems.org/gems/with_model) for Ruby and
|
|
346
|
+
Active Record requirements. RSpec, Minitest, and Test::Unit are optional
|
|
347
|
+
consumer framework dependencies; install the framework used by its selected
|
|
348
|
+
entrypoint. Requiring a selected entrypoint without its framework installed
|
|
349
|
+
raises the framework's ordinary `LoadError`.
|
|
350
|
+
|
|
351
|
+
`scope:` is RSpec-only and is silently ignored by Minitest and Test::Unit.
|
|
256
352
|
|
|
257
353
|
## Thread-safety
|
|
258
354
|
|
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/dsl.rb
CHANGED
|
@@ -13,7 +13,9 @@ module WithModel
|
|
|
13
13
|
#
|
|
14
14
|
# Pass `false` instead of options to create no table at all, so that the
|
|
15
15
|
# model inherits its superclass's table as Rails Single Table Inheritance
|
|
16
|
-
# requires
|
|
16
|
+
# requires, or uses an existing table: the one its model block names with
|
|
17
|
+
# `self.table_name`, or else the one Active Record names after the model.
|
|
18
|
+
# An abstract model needs no table at all. `table(false)` takes no block.
|
|
17
19
|
#
|
|
18
20
|
# @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-create_table
|
|
19
21
|
def table(options = {}, &block)
|
data/lib/with_model/model.rb
CHANGED
|
@@ -73,11 +73,7 @@ module WithModel
|
|
|
73
73
|
# Before `unstub_const`: a teardown that identifies this model's own rows
|
|
74
74
|
# can only do so while the class still has its name.
|
|
75
75
|
table.teardown(@model)
|
|
76
|
-
|
|
77
|
-
cleanup_descendants_tracking
|
|
78
|
-
reset_dependencies_cache
|
|
79
|
-
WithModel::DescendantsTracker.clear([@model])
|
|
80
|
-
@model = nil
|
|
76
|
+
discard_model
|
|
81
77
|
end
|
|
82
78
|
|
|
83
79
|
private
|
|
@@ -129,18 +125,24 @@ module WithModel
|
|
|
129
125
|
def setup_model
|
|
130
126
|
table.configure(@model)
|
|
131
127
|
@model.class_eval(&@model_block) if @model_block
|
|
128
|
+
verify_model
|
|
132
129
|
@model.reset_column_information
|
|
133
130
|
end
|
|
134
131
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
# A model rejected here was never usable, so it is discarded at once rather
|
|
133
|
+
# than left for teardown: there are no rows to clean up, and a caller that
|
|
134
|
+
# does not tear down would otherwise be left with its constant.
|
|
135
|
+
def verify_model
|
|
136
|
+
table.verify(@model)
|
|
137
|
+
rescue InvalidSuperclass
|
|
138
|
+
discard_model
|
|
139
|
+
raise
|
|
138
140
|
end
|
|
139
141
|
|
|
140
|
-
def
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
def discard_model
|
|
143
|
+
stubber.unstub_const
|
|
144
|
+
WithModel::DescendantsTracker.clear([@model])
|
|
145
|
+
@model = nil
|
|
144
146
|
end
|
|
145
147
|
|
|
146
148
|
def stubber
|