Skip to content

Commit 40e4c6b

Browse files
committed
Wrap fixture DSL in FixtureBot.define block with implicit/explicit styles
Fixture files now require a FixtureBot.define { } wrapper, matching the FactoryBot convention. Supports explicit receiver style via block arg (FixtureBot.define { |t| t.user ... }) for editor autocompletion.
1 parent 51300f1 commit 40e4c6b

File tree

3 files changed

+180
-117
lines changed

3 files changed

+180
-117
lines changed

README.md

Lines changed: 95 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -25,32 +25,34 @@ The generated YAML files are static snapshots you can inspect, diff, and commit.
2525
Create `spec/fixtures.rb`:
2626

2727
```ruby
28-
# Generators — these run for every record unless overridden
29-
user.email { "#{name}@example.com" }
30-
31-
# Records
32-
user :brad do
33-
name "Brad"
34-
email "brad@example.com"
35-
end
28+
FixtureBot.define do
29+
# Generators — these run for every record unless overridden
30+
user.email { "#{name}@example.com" }
31+
32+
# Records
33+
user :brad do
34+
name "Brad"
35+
email "brad@example.com"
36+
end
3637

37-
user :alice do
38-
name "Alice"
39-
end
38+
user :alice do
39+
name "Alice"
40+
end
4041

41-
post :hello_world do
42-
title "Hello World"
43-
body "Welcome to the blog!"
44-
author :brad
45-
tags :ruby, :rails
46-
end
42+
post :hello_world do
43+
title "Hello World"
44+
body "Welcome to the blog!"
45+
author :brad
46+
tags :ruby, :rails
47+
end
4748

48-
tag :ruby do
49-
name "Ruby"
50-
end
49+
tag :ruby do
50+
name "Ruby"
51+
end
5152

52-
tag :rails do
53-
name "Rails"
53+
tag :rails do
54+
name "Rails"
55+
end
5456
end
5557
```
5658

@@ -106,21 +108,23 @@ end
106108
Create `test/fixtures.rb`:
107109

108110
```ruby
109-
user.email { "#{name}@example.com" }
111+
FixtureBot.define do
112+
user.email { "#{name}@example.com" }
110113

111-
user :brad do
112-
name "Brad"
113-
email "brad@example.com"
114-
end
114+
user :brad do
115+
name "Brad"
116+
email "brad@example.com"
117+
end
115118

116-
user :alice do
117-
name "Alice"
118-
end
119+
user :alice do
120+
name "Alice"
121+
end
119122

120-
post :hello_world do
121-
title "Hello World"
122-
body "Welcome to the blog!"
123-
author :brad
123+
post :hello_world do
124+
title "Hello World"
125+
body "Welcome to the blog!"
126+
author :brad
127+
end
124128
end
125129
```
126130

@@ -173,27 +177,55 @@ FixtureBot::Schema.define do
173177
end
174178
```
175179

180+
### Implicit vs explicit style
181+
182+
By default, the block is evaluated implicitly — table methods like `user` and `post` are available directly:
183+
184+
```ruby
185+
FixtureBot.define do
186+
user :brad do
187+
name "Brad"
188+
end
189+
end
190+
```
191+
192+
If you prefer an explicit receiver (useful for editor autocompletion or clarity in large files), pass a block argument:
193+
194+
```ruby
195+
FixtureBot.define do |t|
196+
t.user :brad do
197+
name "Brad"
198+
end
199+
end
200+
```
201+
202+
Both styles are equivalent. Record blocks (the inner `do...end`) are always implicit.
203+
176204
### Generators
177205

178206
Set default values for columns. The block runs for each record and has access to the record name:
179207

180208
```ruby
181-
user.email { "#{name}@example.com" }
209+
FixtureBot.define do
210+
user.email { "#{name}@example.com" }
211+
end
182212
```
183213

184214
If a record sets a literal value for that column, it shadows the generator:
185215

186216
```ruby
187-
user.email { "#{name}@example.com" }
217+
FixtureBot.define do
218+
user.email { "#{name}@example.com" }
188219

189-
user :brad do
190-
name "Brad" # generator sees name as "Brad", not "brad"
191-
email "brad@hey.com" # literal — skips the generator entirely
192-
end
220+
user :brad do
221+
name "Brad" # generator sees name as "Brad", not "brad"
222+
email "brad@hey.com" # literal — skips the generator entirely
223+
end
193224

194-
user :alice do
195-
name "Alice"
196-
# email generated as "Alice@example.com"
225+
user :alice do
226+
name "Alice"
227+
# email generated as "Alice@example.com"
228+
end
197229
end
198230
```
199231

@@ -202,27 +234,33 @@ end
202234
Define named records with literal column values:
203235

204236
```ruby
205-
user :brad do
206-
name "Brad"
207-
email "brad@example.com"
237+
FixtureBot.define do
238+
user :brad do
239+
name "Brad"
240+
email "brad@example.com"
241+
end
208242
end
209243
```
210244

211245
Records without a block get an auto-generated ID and any generator defaults:
212246

213247
```ruby
214-
user :alice
215-
# => { id: <stable_id>, email: "alice@example.com" }
248+
FixtureBot.define do
249+
user :alice
250+
# => { id: <stable_id>, email: "alice@example.com" }
251+
end
216252
```
217253

218254
### Associations
219255

220256
Reference other records by name for `belongs_to`:
221257

222258
```ruby
223-
post :hello_world do
224-
title "Hello World"
225-
author :brad # sets author_id to brad's stable ID
259+
FixtureBot.define do
260+
post :hello_world do
261+
title "Hello World"
262+
author :brad # sets author_id to brad's stable ID
263+
end
226264
end
227265
```
228266

@@ -231,9 +269,11 @@ end
231269
Reference multiple records for join table associations:
232270

233271
```ruby
234-
post :hello_world do
235-
title "Hello World"
236-
tags :ruby, :rails # creates rows in posts_tags
272+
FixtureBot.define do
273+
post :hello_world do
274+
title "Hello World"
275+
tags :ruby, :rails # creates rows in posts_tags
276+
end
237277
end
238278
```
239279

@@ -244,5 +284,5 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
244284
Try the playground without Rails:
245285

246286
```bash
247-
bundle exec exe/fixturedump show ./playground/blog
287+
bundle exec exe/fixturebot show ./playground/blog
248288
```

lib/fixturebot.rb

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,37 @@
1616
module FixtureBot
1717
class Error < StandardError; end
1818

19-
def self.define(schema, &block)
20-
definition = Definition.new(schema)
21-
definition.instance_eval(&block)
22-
FixtureSet.new(schema, definition)
19+
# Programmatic API: FixtureBot.define(schema) { ... }
20+
# File API (no schema): FixtureBot.define { ... } — registers block for define_from_file
21+
def self.define(schema = nil, &block)
22+
if schema
23+
definition = Definition.new(schema)
24+
evaluate_block(definition, block)
25+
FixtureSet.new(schema, definition)
26+
else
27+
@pending_blocks ||= []
28+
@pending_blocks << block
29+
nil
30+
end
2331
end
2432

2533
def self.define_from_file(schema, fixtures_path)
34+
@pending_blocks = []
2635
content = File.read(fixtures_path)
36+
eval(content, TOPLEVEL_BINDING, fixtures_path, 1)
37+
2738
definition = Definition.new(schema)
28-
definition.instance_eval(content, fixtures_path, 1)
39+
@pending_blocks.each { |blk| evaluate_block(definition, blk) }
40+
@pending_blocks = nil
2941
FixtureSet.new(schema, definition)
3042
end
43+
44+
def self.evaluate_block(definition, block)
45+
if block.arity > 0
46+
block.call(definition)
47+
else
48+
definition.instance_eval(&block)
49+
end
50+
end
51+
private_class_method :evaluate_block
3152
end

playground/blog/fixtures.rb

Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,60 @@
1-
user.email { "#{name}@blog.test" }
2-
3-
user :brad do
4-
name "Brad"
5-
email "brad@blog.test"
6-
end
7-
8-
user :alice do
9-
name "Alice"
10-
end
11-
12-
user :charlie do
13-
name "Charlie"
14-
end
15-
16-
tag :ruby do
17-
name "Ruby"
18-
end
19-
20-
tag :rails do
21-
name "Rails"
22-
end
23-
24-
tag :testing do
25-
name "Testing"
26-
end
27-
28-
post :hello_world do
29-
title "Hello World"
30-
body "Welcome to the blog!"
31-
author :brad
32-
tags :ruby, :rails
33-
end
34-
35-
post :tdd_guide do
36-
title "Getting Started with TDD"
37-
body "Test-driven development is a practice where you write tests before code."
38-
author :alice
39-
tags :ruby, :testing
40-
end
41-
42-
comment :great_post do
43-
body "Great post, thanks for sharing!"
44-
post :hello_world
45-
author :alice
46-
end
47-
48-
comment :helpful do
49-
body "This was really helpful."
50-
post :tdd_guide
51-
author :charlie
52-
end
53-
54-
comment :follow_up do
55-
body "Could you write a follow-up on mocking?"
56-
post :tdd_guide
57-
author :brad
1+
FixtureBot.define do
2+
user.email { "#{name}@blog.test" }
3+
4+
user :brad do
5+
name "Brad"
6+
email "brad@blog.test"
7+
end
8+
9+
user :alice do
10+
name "Alice"
11+
end
12+
13+
user :charlie do
14+
name "Charlie"
15+
end
16+
17+
tag :ruby do
18+
name "Ruby"
19+
end
20+
21+
tag :rails do
22+
name "Rails"
23+
end
24+
25+
tag :testing do
26+
name "Testing"
27+
end
28+
29+
post :hello_world do
30+
title "Hello World"
31+
body "Welcome to the blog!"
32+
author :brad
33+
tags :ruby, :rails
34+
end
35+
36+
post :tdd_guide do
37+
title "Getting Started with TDD"
38+
body "Test-driven development is a practice where you write tests before code."
39+
author :alice
40+
tags :ruby, :testing
41+
end
42+
43+
comment :great_post do
44+
body "Great post, thanks for sharing!"
45+
post :hello_world
46+
author :alice
47+
end
48+
49+
comment :helpful do
50+
body "This was really helpful."
51+
post :tdd_guide
52+
author :charlie
53+
end
54+
55+
comment :follow_up do
56+
body "Could you write a follow-up on mocking?"
57+
post :tdd_guide
58+
author :brad
59+
end
5860
end

0 commit comments

Comments
 (0)