fixturebot-rails 0.2.0 → 0.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/CHANGELOG.md +19 -0
- data/README.md +177 -1
- data/lib/fixturebot/cli.rb +16 -17
- data/lib/fixturebot/default.rb +24 -9
- data/lib/fixturebot/definition.rb +1 -1
- data/lib/fixturebot/fixture_set.rb +8 -5
- data/lib/fixturebot/key/integer.rb +15 -0
- data/lib/fixturebot/key/uuid.rb +24 -0
- data/lib/fixturebot/key.rb +4 -2
- data/lib/fixturebot/key_registry.rb +23 -0
- data/lib/fixturebot/minitest.rb +3 -0
- data/lib/fixturebot/rails/schema_loader.rb +34 -5
- data/lib/fixturebot/row.rb +55 -35
- data/lib/fixturebot/rspec.rb +3 -0
- data/lib/fixturebot/schema.rb +55 -8
- data/lib/fixturebot/syntax.rb +63 -0
- data/lib/fixturebot/version.rb +1 -1
- data/lib/fixturebot.rb +14 -10
- data/lib/generators/fixturebot/templates/fixtures.rb.tt +2 -0
- data/playground/blog/fixtures.rb +15 -0
- data/playground/blog/schema.rb +5 -0
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8c83c05f009409eb651e9e464fcc70bad3b3abeb377943eef42bfff31b29f9ac
|
|
4
|
+
data.tar.gz: 79973f2828cb2dcb83c4f238ad1023681c0a241d0f7ca1daab13ad1627685f64
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b3c5cfd9eeb975bb9209a2a3e37ca98d7a00225b6c63f638c3f4bdc2a44ff2782eebfcc4616a327d1134d3bcf3dcbbc0a5a661918ab1f4b6fefdffc4ebefed96
|
|
7
|
+
data.tar.gz: 3ea33f5090363d4ee7c8be75720e7fa1a8364c7665304824071d08fd2e5912be7f0d293f9255c7c69f61a0bafc9c06d9ca600954e33d6adb321dba1a748b10eb
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **UUID primary keys.** Tables with UUID primary keys are auto-detected in Rails. For manual schemas, pass `key: FixtureBot::Key::Uuid`. Generates deterministic UUID v5 values.
|
|
8
|
+
- **Polymorphic associations.** Use `votable post(:hello)` to set both `votable_id` and `votable_type: "Post"`. Auto-detected in Rails from `_id`/`_type` column pairs without foreign key constraints. For manual schemas, declare with `polymorphic :votable`.
|
|
9
|
+
- **Custom key strategies.** Any module or object responding to `generate(table_name, record_name)` can be passed as `key:` on a table definition.
|
|
10
|
+
- **Custom primary key columns.** Tables using a column other than `id` as the primary key are auto-detected in Rails. For manual schemas, pass `primary_key: :uid`.
|
|
11
|
+
- **Hardcoded IDs.** Set `id 42` in a record block to override the generated key. Foreign key references resolve to the hardcoded value.
|
|
12
|
+
|
|
13
|
+
### Internals
|
|
14
|
+
|
|
15
|
+
- Key generation strategies (`Key::Integer`, `Key::Uuid`) are stateless modules instead of a single `Key.generate` method.
|
|
16
|
+
- All association types share a unified `resolve(ref, keys)` interface.
|
|
17
|
+
- `KeyRegistry` pre-computes primary key values for all records.
|
|
18
|
+
- `Default::Generator` wraps generator blocks instead of passing raw procs.
|
|
19
|
+
- Foreign key naming conventions (`_id`, `_type`) live as class methods on `BelongsTo` and `PolymorphicBelongsTo`.
|
|
20
|
+
- File-based `define` uses thread-local state instead of module-level `@pending_blocks`.
|
|
21
|
+
|
|
3
22
|
## 0.2.0
|
|
4
23
|
|
|
5
24
|
### Breaking changes
|
data/README.md
CHANGED
|
@@ -9,6 +9,8 @@ FixtureBot lets you define your test data in a Ruby DSL and compiles it into sta
|
|
|
9
9
|
- **Ruby DSL** for defining records, associations, and join tables
|
|
10
10
|
- **Generators** for filling in required columns (like email) across all records
|
|
11
11
|
- **Stable IDs** so foreign keys are consistent and diffs are clean across runs
|
|
12
|
+
- **UUID support** for tables with UUID primary keys
|
|
13
|
+
- **Polymorphic associations** with automatic type column resolution
|
|
12
14
|
- **Schema auto-detection** from your Rails database (no manual column lists)
|
|
13
15
|
- **Auto-generates** before your test suite runs (RSpec and Minitest)
|
|
14
16
|
|
|
@@ -175,7 +177,13 @@ FixtureBot.define do
|
|
|
175
177
|
end
|
|
176
178
|
```
|
|
177
179
|
|
|
178
|
-
The generator block receives a `fixture` object
|
|
180
|
+
The generator block receives a `fixture` object with a `key` method (the record's symbol name). Bare methods inside the block refer to column values set on the record:
|
|
181
|
+
|
|
182
|
+
```ruby
|
|
183
|
+
FixtureBot.define do
|
|
184
|
+
user.display_name { |fixture| "#{fixture.key} (#{email})" }
|
|
185
|
+
end
|
|
186
|
+
```
|
|
179
187
|
|
|
180
188
|
When a generator covers all the columns you need, records don't need a block at all:
|
|
181
189
|
|
|
@@ -225,6 +233,46 @@ FixtureBot.define do
|
|
|
225
233
|
end
|
|
226
234
|
```
|
|
227
235
|
|
|
236
|
+
### Polymorphic associations
|
|
237
|
+
|
|
238
|
+
For polymorphic `belongs_to`, reference the target using its table helper to set both `_id` and `_type` columns:
|
|
239
|
+
|
|
240
|
+
```ruby
|
|
241
|
+
FixtureBot.define do
|
|
242
|
+
post :hello do
|
|
243
|
+
title "Hello"
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
comment :nice do
|
|
247
|
+
body "Nice post"
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
vote :upvote_post do
|
|
251
|
+
votable post(:hello) # sets votable_id and votable_type: "Post"
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
vote :upvote_comment do
|
|
255
|
+
votable comment(:nice) # sets votable_id and votable_type: "Comment"
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
In the manual schema, declare polymorphic associations with `polymorphic`:
|
|
261
|
+
|
|
262
|
+
```ruby
|
|
263
|
+
FixtureBot::Schema.define do
|
|
264
|
+
table :posts, singular: :post, columns: [:title]
|
|
265
|
+
table :comments, singular: :comment, columns: [:body]
|
|
266
|
+
|
|
267
|
+
table :votes, singular: :vote, columns: [:votable_id, :votable_type, :voter_id] do
|
|
268
|
+
polymorphic :votable
|
|
269
|
+
belongs_to :voter, table: :users
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
In Rails, polymorphic associations are auto-detected from `_id`/`_type` column pairs that don't have a foreign key constraint.
|
|
275
|
+
|
|
228
276
|
### Join tables (HABTM)
|
|
229
277
|
|
|
230
278
|
Reference multiple records for join table associations:
|
|
@@ -238,6 +286,100 @@ FixtureBot.define do
|
|
|
238
286
|
end
|
|
239
287
|
```
|
|
240
288
|
|
|
289
|
+
### Hardcoded IDs
|
|
290
|
+
|
|
291
|
+
By default, FixtureBot generates stable IDs deterministically from the table and record name. You can override this with an explicit value:
|
|
292
|
+
|
|
293
|
+
```ruby
|
|
294
|
+
FixtureBot.define do
|
|
295
|
+
user :admin do
|
|
296
|
+
id 42
|
|
297
|
+
name "Admin"
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
post :hello do
|
|
301
|
+
title "Hello"
|
|
302
|
+
author :admin # author_id resolves to 42
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### UUID primary keys
|
|
308
|
+
|
|
309
|
+
Tables with UUID primary keys work automatically in Rails (detected from the column type). In the manual schema, pass `key: FixtureBot::Key::Uuid`:
|
|
310
|
+
|
|
311
|
+
```ruby
|
|
312
|
+
FixtureBot::Schema.define do
|
|
313
|
+
table :projects, singular: :project, columns: [:name], key: FixtureBot::Key::Uuid
|
|
314
|
+
end
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
FixtureBot generates deterministic UUID v5 values, so the output is stable across runs.
|
|
318
|
+
|
|
319
|
+
You can also provide your own key strategy — any object (or module) that responds to `generate(table_name, record_name)`. For example, Stripe-style prefixed IDs:
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
module PrefixedKey
|
|
323
|
+
module_function
|
|
324
|
+
|
|
325
|
+
def generate(table_name, record_name)
|
|
326
|
+
hash = Zlib.crc32("#{table_name}:#{record_name}").to_s(36)
|
|
327
|
+
"#{table_name.to_s.chomp('s')}_#{hash}"
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
FixtureBot::Schema.define do
|
|
332
|
+
table :customers, singular: :customer, columns: [:name], key: PrefixedKey
|
|
333
|
+
# => customer_id: "customer_1a2b3c"
|
|
334
|
+
end
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Custom primary key column
|
|
338
|
+
|
|
339
|
+
If your table uses a column other than `id` as the primary key:
|
|
340
|
+
|
|
341
|
+
```ruby
|
|
342
|
+
FixtureBot::Schema.define do
|
|
343
|
+
table :users, singular: :user, columns: [:name], primary_key: :uid
|
|
344
|
+
end
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
In Rails, this is auto-detected from the database.
|
|
348
|
+
|
|
349
|
+
### Multiple files
|
|
350
|
+
|
|
351
|
+
For larger apps, split fixtures across multiple files using `FixtureBot.require`:
|
|
352
|
+
|
|
353
|
+
```ruby
|
|
354
|
+
# spec/fixtures.rb
|
|
355
|
+
FixtureBot.require "spec/fixtures/**/*.rb"
|
|
356
|
+
|
|
357
|
+
FixtureBot.define do
|
|
358
|
+
user.email { |fixture| "#{fixture.key}@example.com" }
|
|
359
|
+
end
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
```ruby
|
|
363
|
+
# spec/fixtures/users.rb
|
|
364
|
+
FixtureBot.define do
|
|
365
|
+
user :brad do
|
|
366
|
+
name "Brad"
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
```ruby
|
|
372
|
+
# spec/fixtures/posts.rb
|
|
373
|
+
FixtureBot.define do
|
|
374
|
+
post :hello do
|
|
375
|
+
title "Hello"
|
|
376
|
+
author :brad
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Each file calls `FixtureBot.define` with its own block. Files are loaded in alphabetical order. References across files work because everything is resolved after all files are loaded.
|
|
382
|
+
|
|
241
383
|
### Implicit vs explicit style
|
|
242
384
|
|
|
243
385
|
By default, the block is evaluated implicitly. Table methods like `user` and `post` are available directly:
|
|
@@ -280,6 +422,40 @@ FixtureBot::Schema.define do
|
|
|
280
422
|
end
|
|
281
423
|
```
|
|
282
424
|
|
|
425
|
+
## Migrating from FactoryBot
|
|
426
|
+
|
|
427
|
+
FixtureBot provides `build`, `create`, `attributes_for`, and related methods that mirror FactoryBot's API. The key difference is that you pass both a table name and a fixture name instead of just a factory name:
|
|
428
|
+
|
|
429
|
+
```ruby
|
|
430
|
+
# FactoryBot # FixtureBot
|
|
431
|
+
build(:user) # build(:user, :brad)
|
|
432
|
+
create(:user) # create(:user, :brad)
|
|
433
|
+
build(:user, name: "X") # build(:user, :brad, name: "X")
|
|
434
|
+
attributes_for(:user) # attributes_for(:user, :brad)
|
|
435
|
+
build_list(:user, 3) # build_list(:user, :brad, :alice, :bob)
|
|
436
|
+
create_list(:user, 3) # create_list(:user, :brad, :alice, :bob)
|
|
437
|
+
build_pair(:user) # build_pair(:user, :brad, :alice)
|
|
438
|
+
create_pair(:user) # create_pair(:user, :brad, :alice)
|
|
439
|
+
build_stubbed(:user) # build_stubbed(:user, :brad)
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Method reference
|
|
443
|
+
|
|
444
|
+
| Method | Behavior |
|
|
445
|
+
|---|---|
|
|
446
|
+
| `build(:user, :brad, **attrs)` | Duplicates the fixture, applies overrides. Returns unpersisted. |
|
|
447
|
+
| `create(:user, :brad, **attrs)` | Without overrides: returns the fixture (already persisted). With overrides: `build` + `save!`. |
|
|
448
|
+
| `build_stubbed(:user, :brad, **attrs)` | Like `build` but retains `id`. Looks persisted without touching DB. |
|
|
449
|
+
| `attributes_for(:user, :brad, **attrs)` | Returns attributes hash, strips `id`/`created_at`/`updated_at`. |
|
|
450
|
+
| `build_list(:user, :brad, :alice, **attrs)` | Maps each name through `build`. |
|
|
451
|
+
| `create_list(:user, :brad, :alice, **attrs)` | Maps each name through `create`. |
|
|
452
|
+
| `build_pair(:user, :brad, :alice, **attrs)` | Alias for `build_list` with 2 names. |
|
|
453
|
+
| `create_pair(:user, :brad, :alice, **attrs)` | Alias for `create_list` with 2 names. |
|
|
454
|
+
| `build_stubbed_list(:user, :brad, :alice, **attrs)` | Maps each name through `build_stubbed`. |
|
|
455
|
+
| `build_stubbed_pair(:user, :brad, :alice, **attrs)` | Alias for `build_stubbed_list` with 2 names. |
|
|
456
|
+
|
|
457
|
+
These methods are automatically available in your tests when you require `fixturebot/rspec` or `fixturebot/minitest`. They call the standard Rails fixture accessors under the hood, so `build(:user, :brad)` is equivalent to `users(:brad).dup`.
|
|
458
|
+
|
|
283
459
|
## Prior art
|
|
284
460
|
|
|
285
461
|
### [Rails fixtures](https://guides.rubyonrails.org/testing.html#the-low-down-on-fixtures)
|
data/lib/fixturebot/cli.rb
CHANGED
|
@@ -10,14 +10,7 @@ module FixtureBot
|
|
|
10
10
|
|
|
11
11
|
desc "compile DIR", "Compile DIR/schema.rb and DIR/fixtures.rb to YAML fixture files"
|
|
12
12
|
def compile(dir)
|
|
13
|
-
|
|
14
|
-
fixtures_path = File.join(dir, "fixtures.rb")
|
|
15
|
-
|
|
16
|
-
raise Thor::Error, "Schema file not found: #{schema_path}" unless File.exist?(schema_path)
|
|
17
|
-
raise Thor::Error, "Fixtures file not found: #{fixtures_path}" unless File.exist?(fixtures_path)
|
|
18
|
-
|
|
19
|
-
schema = eval(File.read(schema_path), binding, schema_path, 1)
|
|
20
|
-
fixture_set = FixtureBot.define_from_file(schema, fixtures_path)
|
|
13
|
+
fixture_set = load_fixture_set(dir)
|
|
21
14
|
|
|
22
15
|
output_dir = File.join(dir, "fixtures")
|
|
23
16
|
Compiler.new(fixture_set).compile(output_dir)
|
|
@@ -31,15 +24,8 @@ module FixtureBot
|
|
|
31
24
|
|
|
32
25
|
desc "show DIR", "Compile DIR/schema.rb and DIR/fixtures.rb, then print YAML to stdout"
|
|
33
26
|
def show(dir)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
raise Thor::Error, "Schema file not found: #{schema_path}" unless File.exist?(schema_path)
|
|
38
|
-
raise Thor::Error, "Fixtures file not found: #{fixtures_path}" unless File.exist?(fixtures_path)
|
|
39
|
-
|
|
40
|
-
schema = eval(File.read(schema_path), binding, schema_path, 1)
|
|
41
|
-
fixture_set = FixtureBot.define_from_file(schema, fixtures_path)
|
|
42
|
-
compiler = FixtureBot::Compiler.new(fixture_set)
|
|
27
|
+
fixture_set = load_fixture_set(dir)
|
|
28
|
+
compiler = Compiler.new(fixture_set)
|
|
43
29
|
|
|
44
30
|
fixture_set.tables.each do |table_name, records|
|
|
45
31
|
next if records.empty?
|
|
@@ -53,5 +39,18 @@ module FixtureBot
|
|
|
53
39
|
def version
|
|
54
40
|
say "fixturebot #{FixtureBot::VERSION}"
|
|
55
41
|
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def load_fixture_set(dir)
|
|
46
|
+
schema_path = File.join(dir, "schema.rb")
|
|
47
|
+
fixtures_path = File.join(dir, "fixtures.rb")
|
|
48
|
+
|
|
49
|
+
raise Thor::Error, "Schema file not found: #{schema_path}" unless File.exist?(schema_path)
|
|
50
|
+
raise Thor::Error, "Fixtures file not found: #{fixtures_path}" unless File.exist?(fixtures_path)
|
|
51
|
+
|
|
52
|
+
schema = eval(File.read(schema_path), binding, schema_path, 1)
|
|
53
|
+
FixtureBot.define_from_file(schema, fixtures_path)
|
|
54
|
+
end
|
|
56
55
|
end
|
|
57
56
|
end
|
data/lib/fixturebot/default.rb
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
module FixtureBot
|
|
4
4
|
module Default
|
|
5
|
-
Fixture = Data.define(:key)
|
|
6
|
-
|
|
7
5
|
class Definition
|
|
8
6
|
def initialize(table, defaults)
|
|
9
7
|
@defaults = defaults
|
|
@@ -16,22 +14,39 @@ module FixtureBot
|
|
|
16
14
|
table.columns.each do |col|
|
|
17
15
|
define_singleton_method(col) do |&block|
|
|
18
16
|
raise ArgumentError, "#{col} requires a block" unless block
|
|
19
|
-
@defaults[col] = block
|
|
17
|
+
@defaults[col] = Generator.new(block)
|
|
20
18
|
end
|
|
21
19
|
end
|
|
22
20
|
end
|
|
23
21
|
end
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
Fixture = Data.define(:key)
|
|
24
|
+
|
|
25
|
+
class Generator
|
|
26
|
+
def initialize(block)
|
|
27
|
+
@block = block
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def generate(record_name, literal_values)
|
|
31
|
+
context = Context.new(literal_values)
|
|
32
|
+
context.instance_exec(Fixture.new(key: record_name), &@block)
|
|
28
33
|
end
|
|
29
34
|
|
|
30
35
|
private
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
Context = Struct.new(:literal_values) do
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def method_missing(name, *)
|
|
41
|
+
if literal_values.key?(name)
|
|
42
|
+
literal_values[name]
|
|
43
|
+
else
|
|
44
|
+
super
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def respond_to_missing?(name, *)
|
|
49
|
+
literal_values.key?(name) || super
|
|
35
50
|
end
|
|
36
51
|
end
|
|
37
52
|
end
|
|
@@ -33,7 +33,7 @@ module FixtureBot
|
|
|
33
33
|
row_def = Row::Definition.new(table, @schema)
|
|
34
34
|
row_def.instance_eval(&block) if block
|
|
35
35
|
@rows << Row::Declaration.new(
|
|
36
|
-
|
|
36
|
+
table_name: table.name,
|
|
37
37
|
name: record_name,
|
|
38
38
|
literal_values: row_def.literal_values,
|
|
39
39
|
association_refs: row_def.association_refs,
|
|
@@ -10,18 +10,21 @@ module FixtureBot
|
|
|
10
10
|
schema.tables.each_key { |name| @tables[name] = {} }
|
|
11
11
|
schema.join_tables.each_key { |name| @tables[name] = {} }
|
|
12
12
|
|
|
13
|
+
keys = KeyRegistry.new(definition.rows, schema.tables)
|
|
14
|
+
|
|
13
15
|
definition.rows.each do |row|
|
|
14
16
|
builder = Row::Builder.new(
|
|
15
17
|
row: row,
|
|
16
|
-
table: schema.tables[row.
|
|
17
|
-
defaults: definition.defaults[row.
|
|
18
|
-
join_tables: schema.join_tables
|
|
18
|
+
table: schema.tables[row.table_name],
|
|
19
|
+
defaults: definition.defaults[row.table_name],
|
|
20
|
+
join_tables: schema.join_tables,
|
|
21
|
+
keys: keys
|
|
19
22
|
)
|
|
20
23
|
|
|
21
|
-
@tables[row.
|
|
24
|
+
@tables[row.table_name][row.name] = builder.record
|
|
22
25
|
|
|
23
26
|
builder.join_rows.each do |join_row|
|
|
24
|
-
@tables[join_row
|
|
27
|
+
@tables[join_row.join_table][join_row.key] = join_row.row
|
|
25
28
|
end
|
|
26
29
|
end
|
|
27
30
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest/sha1"
|
|
4
|
+
|
|
5
|
+
module FixtureBot
|
|
6
|
+
module Key
|
|
7
|
+
module Uuid
|
|
8
|
+
NAMESPACE = "fixturebot"
|
|
9
|
+
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def generate(table_name, record_name)
|
|
13
|
+
digest = Digest::SHA1.digest("#{NAMESPACE}:#{table_name}:#{record_name}")
|
|
14
|
+
# Set version 5 (bits 4-7 of byte 6)
|
|
15
|
+
digest[6] = ((digest[6].ord & 0x0f) | 0x50).chr
|
|
16
|
+
# Set variant (bits 6-7 of byte 8)
|
|
17
|
+
digest[8] = ((digest[8].ord & 0x3f) | 0x80).chr
|
|
18
|
+
|
|
19
|
+
hex = digest[0, 16].unpack1("H*")
|
|
20
|
+
"#{hex[0, 8]}-#{hex[8, 4]}-#{hex[12, 4]}-#{hex[16, 4]}-#{hex[20, 12]}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/fixturebot/key.rb
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative "key/integer"
|
|
4
|
+
require_relative "key/uuid"
|
|
4
5
|
|
|
5
6
|
module FixtureBot
|
|
6
7
|
module Key
|
|
8
|
+
# Backward compatibility
|
|
7
9
|
def self.generate(table_name, record_name)
|
|
8
|
-
|
|
10
|
+
Integer.generate(table_name, record_name)
|
|
9
11
|
end
|
|
10
12
|
end
|
|
11
13
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FixtureBot
|
|
4
|
+
class KeyRegistry
|
|
5
|
+
def initialize(rows, schema_tables)
|
|
6
|
+
@values = {}
|
|
7
|
+
|
|
8
|
+
rows.each do |row|
|
|
9
|
+
table = schema_tables[row.table_name]
|
|
10
|
+
pk_col = table.primary_key
|
|
11
|
+
@values[[row.table_name, row.name]] = if row.literal_values.key?(pk_col)
|
|
12
|
+
row.literal_values[pk_col]
|
|
13
|
+
else
|
|
14
|
+
table.key.generate(row.table_name, row.name)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def resolve(table_name, record_name)
|
|
20
|
+
@values.fetch([table_name, record_name])
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/fixturebot/minitest.rb
CHANGED
|
@@ -38,11 +38,15 @@ module FixtureBot
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def build_table(name)
|
|
41
|
+
pk_column_name = @connection.primary_key(name) || "id"
|
|
42
|
+
pk_column = @connection.columns(name).find { |c| c.name == pk_column_name }
|
|
43
|
+
key = pk_column && pk_column.type == :uuid ? Key::Uuid : Key::Integer
|
|
44
|
+
|
|
41
45
|
columns = @connection.columns(name)
|
|
42
|
-
.reject { |c|
|
|
46
|
+
.reject { |c| c.name == pk_column_name || timestamp_column?(c.name) }
|
|
43
47
|
.map { |c| c.name.to_sym }
|
|
44
48
|
|
|
45
|
-
|
|
49
|
+
fk_associations = @connection.foreign_keys(name).map do |fk|
|
|
46
50
|
Schema::BelongsTo.new(
|
|
47
51
|
name: association_name(fk.column),
|
|
48
52
|
table: fk.to_table.to_sym,
|
|
@@ -50,11 +54,17 @@ module FixtureBot
|
|
|
50
54
|
)
|
|
51
55
|
end
|
|
52
56
|
|
|
57
|
+
polymorphic_associations = detect_polymorphic_associations(name, columns, fk_associations)
|
|
58
|
+
|
|
59
|
+
associations = fk_associations + polymorphic_associations
|
|
60
|
+
|
|
53
61
|
Schema::Table.new(
|
|
54
62
|
name: name.to_sym,
|
|
55
63
|
singular_name: singularize(name),
|
|
56
64
|
columns: columns,
|
|
57
|
-
|
|
65
|
+
associations: associations,
|
|
66
|
+
key: key,
|
|
67
|
+
primary_key: pk_column_name.to_sym
|
|
58
68
|
)
|
|
59
69
|
end
|
|
60
70
|
|
|
@@ -74,8 +84,8 @@ module FixtureBot
|
|
|
74
84
|
@connection.tables - %w[ar_internal_metadata schema_migrations]
|
|
75
85
|
end
|
|
76
86
|
|
|
77
|
-
def
|
|
78
|
-
%w[
|
|
87
|
+
def timestamp_column?(name)
|
|
88
|
+
%w[created_at updated_at].include?(name)
|
|
79
89
|
end
|
|
80
90
|
|
|
81
91
|
def foreign_key_column?(column)
|
|
@@ -110,6 +120,25 @@ module FixtureBot
|
|
|
110
120
|
foreign_key_columns(name).size == 2
|
|
111
121
|
end
|
|
112
122
|
end
|
|
123
|
+
|
|
124
|
+
def detect_polymorphic_associations(table_name, columns, fk_associations)
|
|
125
|
+
fk_column_names = fk_associations.map { |a| a.foreign_key }
|
|
126
|
+
id_columns = columns.select { |c| c.to_s.end_with?("_id") }
|
|
127
|
+
|
|
128
|
+
id_columns.filter_map do |id_col|
|
|
129
|
+
next if fk_column_names.include?(id_col)
|
|
130
|
+
|
|
131
|
+
name = id_col.to_s.sub(/_id$/, "").to_sym
|
|
132
|
+
type_col = Schema::PolymorphicBelongsTo.foreign_type(name)
|
|
133
|
+
next unless columns.include?(type_col)
|
|
134
|
+
|
|
135
|
+
Schema::PolymorphicBelongsTo.new(
|
|
136
|
+
name: name,
|
|
137
|
+
foreign_key: id_col,
|
|
138
|
+
foreign_type: type_col
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
113
142
|
end
|
|
114
143
|
end
|
|
115
144
|
end
|
data/lib/fixturebot/row.rb
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module FixtureBot
|
|
4
4
|
module Row
|
|
5
|
-
|
|
5
|
+
TableReference = Data.define(:table_name, :record_name)
|
|
6
|
+
TagRef = Data.define(:other_table, :refs)
|
|
7
|
+
JoinRow = Data.define(:key, :join_table, :row)
|
|
8
|
+
Declaration = Data.define(:table_name, :name, :literal_values, :association_refs, :tag_refs)
|
|
6
9
|
|
|
7
10
|
class Definition
|
|
8
11
|
attr_reader :literal_values, :association_refs, :tag_refs
|
|
@@ -13,8 +16,10 @@ module FixtureBot
|
|
|
13
16
|
@tag_refs = {}
|
|
14
17
|
|
|
15
18
|
define_column_methods(table)
|
|
19
|
+
define_primary_key_method(table)
|
|
16
20
|
define_association_methods(table)
|
|
17
21
|
define_join_table_methods(table, schema)
|
|
22
|
+
define_table_reference_methods(schema, table)
|
|
18
23
|
end
|
|
19
24
|
|
|
20
25
|
private
|
|
@@ -27,8 +32,15 @@ module FixtureBot
|
|
|
27
32
|
end
|
|
28
33
|
end
|
|
29
34
|
|
|
35
|
+
def define_primary_key_method(table)
|
|
36
|
+
pk_col = table.primary_key
|
|
37
|
+
define_singleton_method(pk_col) do |value|
|
|
38
|
+
@literal_values[pk_col] = value
|
|
39
|
+
end unless respond_to?(pk_col)
|
|
40
|
+
end
|
|
41
|
+
|
|
30
42
|
def define_association_methods(table)
|
|
31
|
-
table.
|
|
43
|
+
table.associations.each do |assoc|
|
|
32
44
|
define_singleton_method(assoc.name) do |ref|
|
|
33
45
|
@association_refs[assoc.name] = ref
|
|
34
46
|
end
|
|
@@ -39,48 +51,58 @@ module FixtureBot
|
|
|
39
51
|
schema.join_tables.each_value do |jt|
|
|
40
52
|
if jt.left_table == table.name
|
|
41
53
|
define_singleton_method(jt.right_table) do |*refs|
|
|
42
|
-
@tag_refs[jt.name] =
|
|
54
|
+
@tag_refs[jt.name] = TagRef.new(other_table: jt.right_table, refs: refs)
|
|
43
55
|
end
|
|
44
56
|
elsif jt.right_table == table.name
|
|
45
57
|
define_singleton_method(jt.left_table) do |*refs|
|
|
46
|
-
@tag_refs[jt.name] =
|
|
58
|
+
@tag_refs[jt.name] = TagRef.new(other_table: jt.left_table, refs: refs)
|
|
47
59
|
end
|
|
48
60
|
end
|
|
49
61
|
end
|
|
50
62
|
end
|
|
63
|
+
|
|
64
|
+
def define_table_reference_methods(schema, table)
|
|
65
|
+
schema.tables.each_value do |t|
|
|
66
|
+
method_name = t.singular_name
|
|
67
|
+
next if respond_to?(method_name)
|
|
68
|
+
define_singleton_method(method_name) do |record_name|
|
|
69
|
+
TableReference.new(table_name: t.name, record_name: record_name)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
51
73
|
end
|
|
52
74
|
|
|
53
75
|
class Builder
|
|
54
|
-
def initialize(row:, table:, defaults:, join_tables:)
|
|
76
|
+
def initialize(row:, table:, defaults:, join_tables:, keys:)
|
|
55
77
|
@row = row
|
|
56
78
|
@table = table
|
|
57
79
|
@defaults = defaults
|
|
58
80
|
@join_tables = join_tables
|
|
81
|
+
@keys = keys
|
|
59
82
|
end
|
|
60
83
|
|
|
61
|
-
def
|
|
62
|
-
@
|
|
84
|
+
def primary_key_value
|
|
85
|
+
@primary_key_value ||= @keys.resolve(@row.table_name, @row.name)
|
|
63
86
|
end
|
|
64
87
|
|
|
65
88
|
def record
|
|
66
|
-
|
|
89
|
+
values = {}
|
|
90
|
+
.merge!(defaulted_values)
|
|
91
|
+
.merge!(association_values)
|
|
92
|
+
.merge!(@row.literal_values)
|
|
93
|
+
|
|
94
|
+
result = { @table.primary_key => primary_key_value }
|
|
67
95
|
@table.columns.each do |col|
|
|
68
|
-
if
|
|
69
|
-
result[col] = @row.literal_values[col]
|
|
70
|
-
elsif foreign_key_values.key?(col)
|
|
71
|
-
result[col] = foreign_key_values[col]
|
|
72
|
-
elsif defaulted_values.key?(col)
|
|
73
|
-
result[col] = defaulted_values[col]
|
|
74
|
-
end
|
|
96
|
+
result[col] = values[col] if values.key?(col)
|
|
75
97
|
end
|
|
76
98
|
result
|
|
77
99
|
end
|
|
78
100
|
|
|
79
101
|
def join_rows
|
|
80
|
-
@row.tag_refs.flat_map do |join_table_name,
|
|
102
|
+
@row.tag_refs.flat_map do |join_table_name, tag_ref|
|
|
81
103
|
jt = @join_tables[join_table_name]
|
|
82
|
-
|
|
83
|
-
build_join_row(jt,
|
|
104
|
+
tag_ref.refs.map do |ref|
|
|
105
|
+
build_join_row(jt, tag_ref.other_table, ref)
|
|
84
106
|
end
|
|
85
107
|
end
|
|
86
108
|
end
|
|
@@ -88,38 +110,36 @@ module FixtureBot
|
|
|
88
110
|
private
|
|
89
111
|
|
|
90
112
|
def build_join_row(jt, other_table, tag_ref)
|
|
91
|
-
other_id =
|
|
113
|
+
other_id = @keys.resolve(other_table, tag_ref)
|
|
92
114
|
|
|
93
|
-
if jt.left_table == @row.
|
|
94
|
-
|
|
115
|
+
if jt.left_table == @row.table_name
|
|
116
|
+
JoinRow.new(
|
|
95
117
|
key: :"#{@row.name}_#{tag_ref}",
|
|
96
118
|
join_table: jt.name,
|
|
97
|
-
row: { jt.left_foreign_key =>
|
|
98
|
-
|
|
119
|
+
row: { jt.left_foreign_key => primary_key_value, jt.right_foreign_key => other_id }
|
|
120
|
+
)
|
|
99
121
|
else
|
|
100
|
-
|
|
122
|
+
JoinRow.new(
|
|
101
123
|
key: :"#{tag_ref}_#{@row.name}",
|
|
102
124
|
join_table: jt.name,
|
|
103
|
-
row: { jt.left_foreign_key => other_id, jt.right_foreign_key =>
|
|
104
|
-
|
|
125
|
+
row: { jt.left_foreign_key => other_id, jt.right_foreign_key => primary_key_value }
|
|
126
|
+
)
|
|
105
127
|
end
|
|
106
128
|
end
|
|
107
129
|
|
|
108
|
-
def
|
|
109
|
-
@
|
|
110
|
-
assoc = @table.
|
|
111
|
-
hash
|
|
130
|
+
def association_values
|
|
131
|
+
@association_values ||= @row.association_refs.each_with_object({}) do |(assoc_name, ref), hash|
|
|
132
|
+
assoc = @table.associations.find { |a| a.name == assoc_name }
|
|
133
|
+
hash.merge!(assoc.resolve(ref, @keys))
|
|
112
134
|
end
|
|
113
135
|
end
|
|
114
136
|
|
|
115
137
|
def defaulted_values
|
|
116
|
-
@defaulted_values ||= @defaults.each_with_object({}) do |(col,
|
|
138
|
+
@defaulted_values ||= @defaults.each_with_object({}) do |(col, generator), result|
|
|
117
139
|
next if @row.literal_values.key?(col)
|
|
118
|
-
next if
|
|
140
|
+
next if association_values.key?(col)
|
|
119
141
|
|
|
120
|
-
|
|
121
|
-
context = Default::Context.new(literal_values: @row.literal_values)
|
|
122
|
-
result[col] = context.instance_exec(fixture, &block)
|
|
142
|
+
result[col] = generator.generate(@row.name, @row.literal_values)
|
|
123
143
|
end
|
|
124
144
|
end
|
|
125
145
|
end
|
data/lib/fixturebot/rspec.rb
CHANGED
data/lib/fixturebot/schema.rb
CHANGED
|
@@ -2,8 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
module FixtureBot
|
|
4
4
|
class Schema
|
|
5
|
-
Table = Data.define(:name, :singular_name, :columns, :
|
|
6
|
-
|
|
5
|
+
Table = Data.define(:name, :singular_name, :columns, :associations, :key, :primary_key) do
|
|
6
|
+
def initialize(name:, singular_name:, columns:, associations: [], key: Key::Integer, primary_key: :id)
|
|
7
|
+
super
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def belongs_to_associations
|
|
11
|
+
associations.reject(&:polymorphic?)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def polymorphic_associations
|
|
15
|
+
associations.select(&:polymorphic?)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
BelongsTo = Data.define(:name, :table, :foreign_key) do
|
|
20
|
+
def self.foreign_key(name) = :"#{name}_id"
|
|
21
|
+
|
|
22
|
+
def polymorphic? = false
|
|
23
|
+
|
|
24
|
+
def resolve(ref, keys)
|
|
25
|
+
{ foreign_key => keys.resolve(table, ref) }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
PolymorphicBelongsTo = Data.define(:name, :foreign_key, :foreign_type) do
|
|
30
|
+
def self.foreign_key(name) = :"#{name}_id"
|
|
31
|
+
def self.foreign_type(name) = :"#{name}_type"
|
|
32
|
+
|
|
33
|
+
def polymorphic? = true
|
|
34
|
+
|
|
35
|
+
def resolve(table_ref, keys)
|
|
36
|
+
require "active_support/inflector" unless defined?(ActiveSupport::Inflector)
|
|
37
|
+
|
|
38
|
+
{
|
|
39
|
+
foreign_key => keys.resolve(table_ref.table_name, table_ref.record_name),
|
|
40
|
+
foreign_type => ActiveSupport::Inflector.classify(table_ref.table_name.to_s)
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
7
45
|
JoinTable = Data.define(:name, :left_table, :right_table, :left_foreign_key, :right_foreign_key)
|
|
8
46
|
|
|
9
47
|
attr_reader :tables, :join_tables
|
|
@@ -33,7 +71,7 @@ module FixtureBot
|
|
|
33
71
|
@schema = schema
|
|
34
72
|
end
|
|
35
73
|
|
|
36
|
-
def table(name, singular:, columns: [], &block)
|
|
74
|
+
def table(name, singular:, columns: [], key: Key::Integer, primary_key: :id, &block)
|
|
37
75
|
associations = []
|
|
38
76
|
if block
|
|
39
77
|
table_builder = TableBuilder.new(associations)
|
|
@@ -43,7 +81,9 @@ module FixtureBot
|
|
|
43
81
|
name: name,
|
|
44
82
|
singular_name: singular,
|
|
45
83
|
columns: columns,
|
|
46
|
-
|
|
84
|
+
associations: associations,
|
|
85
|
+
key: key,
|
|
86
|
+
primary_key: primary_key
|
|
47
87
|
))
|
|
48
88
|
end
|
|
49
89
|
|
|
@@ -54,8 +94,8 @@ module FixtureBot
|
|
|
54
94
|
name: name,
|
|
55
95
|
left_table: left_table,
|
|
56
96
|
right_table: right_table,
|
|
57
|
-
left_foreign_key:
|
|
58
|
-
right_foreign_key:
|
|
97
|
+
left_foreign_key: BelongsTo.foreign_key(left_singular),
|
|
98
|
+
right_foreign_key: BelongsTo.foreign_key(right_singular)
|
|
59
99
|
))
|
|
60
100
|
end
|
|
61
101
|
end
|
|
@@ -66,8 +106,15 @@ module FixtureBot
|
|
|
66
106
|
end
|
|
67
107
|
|
|
68
108
|
def belongs_to(name, table:)
|
|
69
|
-
|
|
70
|
-
|
|
109
|
+
@associations << BelongsTo.new(name: name, table: table, foreign_key: BelongsTo.foreign_key(name))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def polymorphic(name)
|
|
113
|
+
@associations << PolymorphicBelongsTo.new(
|
|
114
|
+
name: name,
|
|
115
|
+
foreign_key: PolymorphicBelongsTo.foreign_key(name),
|
|
116
|
+
foreign_type: PolymorphicBelongsTo.foreign_type(name)
|
|
117
|
+
)
|
|
71
118
|
end
|
|
72
119
|
end
|
|
73
120
|
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/string/inflections"
|
|
4
|
+
require "active_support/core_ext/hash/keys"
|
|
5
|
+
|
|
6
|
+
module FixtureBot
|
|
7
|
+
module Syntax
|
|
8
|
+
module Methods
|
|
9
|
+
def build(table_name, fixture_name, **attributes)
|
|
10
|
+
record = send(table_name.to_s.pluralize, fixture_name).dup
|
|
11
|
+
record.assign_attributes(attributes) if attributes.any?
|
|
12
|
+
record
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def create(table_name, fixture_name, **attributes)
|
|
16
|
+
if attributes.any?
|
|
17
|
+
build(table_name, fixture_name, **attributes).tap(&:save!)
|
|
18
|
+
else
|
|
19
|
+
send(table_name.to_s.pluralize, fixture_name)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def build_stubbed(table_name, fixture_name, **attributes)
|
|
24
|
+
source = send(table_name.to_s.pluralize, fixture_name)
|
|
25
|
+
attrs = source.attributes
|
|
26
|
+
attrs.merge!(attributes.stringify_keys) if attributes.any?
|
|
27
|
+
source.class.instantiate(attrs)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def attributes_for(table_name, fixture_name, **attributes)
|
|
31
|
+
send(table_name.to_s.pluralize, fixture_name)
|
|
32
|
+
.attributes
|
|
33
|
+
.symbolize_keys
|
|
34
|
+
.except(:id, :created_at, :updated_at)
|
|
35
|
+
.merge(attributes)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def build_list(table_name, *fixture_names, **attributes)
|
|
39
|
+
fixture_names.map { |name| build(table_name, name, **attributes) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def create_list(table_name, *fixture_names, **attributes)
|
|
43
|
+
fixture_names.map { |name| create(table_name, name, **attributes) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def build_pair(table_name, *fixture_names, **attributes)
|
|
47
|
+
build_list(table_name, *fixture_names, **attributes)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def create_pair(table_name, *fixture_names, **attributes)
|
|
51
|
+
create_list(table_name, *fixture_names, **attributes)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def build_stubbed_list(table_name, *fixture_names, **attributes)
|
|
55
|
+
fixture_names.map { |name| build_stubbed(table_name, name, **attributes) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def build_stubbed_pair(table_name, *fixture_names, **attributes)
|
|
59
|
+
build_stubbed_list(table_name, *fixture_names, **attributes)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/fixturebot/version.rb
CHANGED
data/lib/fixturebot.rb
CHANGED
|
@@ -6,6 +6,7 @@ require_relative "fixturebot/key"
|
|
|
6
6
|
require_relative "fixturebot/default"
|
|
7
7
|
require_relative "fixturebot/row"
|
|
8
8
|
require_relative "fixturebot/definition"
|
|
9
|
+
require_relative "fixturebot/key_registry"
|
|
9
10
|
require_relative "fixturebot/fixture_set"
|
|
10
11
|
require_relative "fixturebot/compiler"
|
|
11
12
|
require_relative "fixturebot/cli"
|
|
@@ -14,28 +15,31 @@ module FixtureBot
|
|
|
14
15
|
class Error < StandardError; end
|
|
15
16
|
|
|
16
17
|
# Programmatic API: FixtureBot.define(schema) { ... }
|
|
17
|
-
# File API (no schema): FixtureBot.define { ... } —
|
|
18
|
+
# File API (no schema): FixtureBot.define { ... } — evaluated against current_definition if set
|
|
18
19
|
def self.define(schema = nil, &block)
|
|
19
20
|
if schema
|
|
20
21
|
definition = Definition.new(schema)
|
|
21
22
|
evaluate_block(definition, block)
|
|
22
23
|
FixtureSet.new(schema, definition)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@pending_blocks << block
|
|
24
|
+
elsif Thread.current[:fixturebot_definition]
|
|
25
|
+
evaluate_block(Thread.current[:fixturebot_definition], block)
|
|
26
26
|
nil
|
|
27
|
+
else
|
|
28
|
+
raise Error, "FixtureBot.define without a schema must be called from within define_from_file"
|
|
27
29
|
end
|
|
28
30
|
end
|
|
29
31
|
|
|
30
|
-
def self.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
eval(content, TOPLEVEL_BINDING, fixtures_path, 1)
|
|
32
|
+
def self.require(glob)
|
|
33
|
+
Dir.glob(glob).sort.each { |f| load f }
|
|
34
|
+
end
|
|
34
35
|
|
|
36
|
+
def self.define_from_file(schema, fixtures_path)
|
|
35
37
|
definition = Definition.new(schema)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
Thread.current[:fixturebot_definition] = definition
|
|
39
|
+
eval(File.read(fixtures_path), TOPLEVEL_BINDING, fixtures_path, 1)
|
|
38
40
|
FixtureSet.new(schema, definition)
|
|
41
|
+
ensure
|
|
42
|
+
Thread.current[:fixturebot_definition] = nil
|
|
39
43
|
end
|
|
40
44
|
|
|
41
45
|
def self.evaluate_block(definition, block)
|
data/playground/blog/fixtures.rb
CHANGED
|
@@ -51,4 +51,19 @@ FixtureBot.define do
|
|
|
51
51
|
post :tdd_guide
|
|
52
52
|
author :brad
|
|
53
53
|
end
|
|
54
|
+
|
|
55
|
+
vote :alice_likes_hello do
|
|
56
|
+
votable post(:hello_world)
|
|
57
|
+
voter :alice
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
vote :brad_likes_tdd do
|
|
61
|
+
votable post(:tdd_guide)
|
|
62
|
+
voter :brad
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
vote :charlie_likes_great_post do
|
|
66
|
+
votable comment(:great_post)
|
|
67
|
+
voter :charlie
|
|
68
|
+
end
|
|
54
69
|
end
|
data/playground/blog/schema.rb
CHANGED
|
@@ -13,4 +13,9 @@ FixtureBot::Schema.define do
|
|
|
13
13
|
table :tags, singular: :tag, columns: [:name]
|
|
14
14
|
|
|
15
15
|
join_table :posts_tags, :posts, :tags
|
|
16
|
+
|
|
17
|
+
table :votes, singular: :vote, columns: [:votable_id, :votable_type, :voter_id] do
|
|
18
|
+
polymorphic :votable
|
|
19
|
+
belongs_to :voter, table: :users
|
|
20
|
+
end
|
|
16
21
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fixturebot-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brad Gessler
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-03-11 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activerecord
|
|
@@ -74,6 +74,9 @@ files:
|
|
|
74
74
|
- lib/fixturebot/definition.rb
|
|
75
75
|
- lib/fixturebot/fixture_set.rb
|
|
76
76
|
- lib/fixturebot/key.rb
|
|
77
|
+
- lib/fixturebot/key/integer.rb
|
|
78
|
+
- lib/fixturebot/key/uuid.rb
|
|
79
|
+
- lib/fixturebot/key_registry.rb
|
|
77
80
|
- lib/fixturebot/minitest.rb
|
|
78
81
|
- lib/fixturebot/rails.rb
|
|
79
82
|
- lib/fixturebot/rails/cli.rb
|
|
@@ -82,6 +85,7 @@ files:
|
|
|
82
85
|
- lib/fixturebot/row.rb
|
|
83
86
|
- lib/fixturebot/rspec.rb
|
|
84
87
|
- lib/fixturebot/schema.rb
|
|
88
|
+
- lib/fixturebot/syntax.rb
|
|
85
89
|
- lib/fixturebot/version.rb
|
|
86
90
|
- lib/generators/fixturebot/install_generator.rb
|
|
87
91
|
- lib/generators/fixturebot/templates/fixtures.rb.tt
|