factory_bot-with 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 330ba97b29d7109bb0cb89cf0493cced5300e17c3a1465066405e46c66cbcd76
4
- data.tar.gz: 504cbcee6a16323d2a9494f10bcb68754d84130b343c316eab7a9f4c01432a0c
3
+ metadata.gz: 880e6e75c055c95bf4c8afab93288afaeb01f1a8ea7d162ecec04c3adaba1498
4
+ data.tar.gz: d59eb2dbc2c24256e1c1b4053d194de41378cdd3773c06375bf8617ae63af642
5
5
  SHA512:
6
- metadata.gz: 11ed1a26a0ebd2590398eef94ebbf8aaeb23419cdd2b0d5e3979f3a64bbe4179e1f96d731804ccbd042535806a4999de62f5a6d5b72cf7ff5c2fbf379cd30372
7
- data.tar.gz: 5da17d95d18e8c746ddd5ce0a1213a8cf24dec8766753655ea912bc91a6b7c38dde9c7d85d73bca4165b7d17f126af31c2a4fd71be3cdbb5807c40a0434ef259
6
+ metadata.gz: 74ee4b6d1718cfef30a3ddfaaca2486f854c8c6db5c4b1fa63c1947f2ae3f2430e8ec67afd8dac052ff049df4d61c7f091b66a0080d187e51e58c4795534b66f
7
+ data.tar.gz: f01718b24e04fe024189d77cec858f0aad8ec88fac9de520ced36bf954863775e6c0ecb079d0831016fac2d863edec204c8a23bec82f304ecd2b8c4186c9ab5e
@@ -14,7 +14,8 @@ jobs:
14
14
  strategy:
15
15
  matrix:
16
16
  ruby:
17
- - '3.3.6'
17
+ - '3.4.2'
18
+ - '3.3.7'
18
19
  - '3.2.6'
19
20
 
20
21
  steps:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0]
4
+
5
+ - Fixed: Improved error message for incorrect factory usage
6
+ - Added: Added `with` scope syntax for automatic association resolution
7
+
3
8
  ## [0.3.0] - 2024-12-09
4
9
 
5
10
  - Added: Added `FactoryBot::With.register_strategy` to support custom strategies
data/README.md CHANGED
@@ -5,6 +5,8 @@
5
5
 
6
6
  FactoryBot::With is a FactoryBot extension that wraps `FactoryBot::Syntax::Methods` to make it a little easier to use.
7
7
 
8
+ [FactoryBot における関連の扱いと、factory_bot-with gem を作った話 (Japanese)](https://zenn.dev/yubrot/articles/032447068e308e)
9
+
8
10
  For example, with these factories:
9
11
 
10
12
  ```ruby
@@ -143,16 +145,18 @@ create.blog(with.article) # autocomplete to :blog_article
143
145
 
144
146
  </details>
145
147
 
148
+ ## Additional features
149
+
146
150
  ### `with` as a template
147
151
 
148
- `with` can also be used stand-alone. It works as a template for factory method calls.
152
+ `with` can also be used stand-alone. Stand-alone `with` can be used in place of the factory name. It works as a template for factory method calls.
149
153
 
150
154
  Instead of writing:
151
155
 
152
156
  ```ruby
153
157
  let(:story) { create(:story, *story_args, **story_kwargs) }
154
158
  let(:story_args) { [] }
155
- let(:story_kwargs) { {} }
159
+ let(:story_kwargs) { { category: "SF" } }
156
160
 
157
161
  context "when published more than one year ago" do
158
162
  let(:story_args) { [*super(), :published] }
@@ -167,7 +171,7 @@ You can write like this:
167
171
  ```ruby
168
172
  # Factory methods accept a With instance as a first argument:
169
173
  let(:story) { create(story_template) }
170
- let(:story_template) { with.story }
174
+ let(:story_template) { with.story(category: "SF") }
171
175
 
172
176
  context "when published more than one year ago" do
173
177
  let(:story_template) { with(super(), :published, start_at: 2.year.ago) }
@@ -176,6 +180,22 @@ context "when published more than one year ago" do
176
180
  end
177
181
  ```
178
182
 
183
+ ### `with` scope for automatic association resolution
184
+
185
+ By calling `with` without positional arguments, but with keyword arguments that define the relationship between factory names and objects, along with a block, `factory_bot-with` creates a scope where those objects become candidates for automatic association resolution.
186
+
187
+ ```ruby
188
+ let(:blog) { create.blog }
189
+
190
+ before do
191
+ with(blog:) do
192
+ # Just like when using create.blog, blog is automatically resolved when create.article is called
193
+ create.article(with.comment)
194
+ create.article(with_list.comment(3))
195
+ end
196
+ end
197
+ ```
198
+
179
199
  ## Development
180
200
 
181
201
  ```bash
@@ -4,31 +4,28 @@ module FactoryBot
4
4
  class With
5
5
  # An intermediate object to provide some notation combined with <code>method_missing</code>.
6
6
  # @example
7
- # class Foo
8
- # def foo(name = nil)
7
+ # class Example
8
+ # def foo(name = nil, ...)
9
9
  # return FactoryBot::With::Proxy.new(self, __method__) unless name
10
10
  #
11
11
  # name
12
12
  # end
13
13
  # end
14
14
  #
15
- # Foo.new.foo.bar #=> :bar
15
+ # ex = Example.new
16
+ # ex.foo.bar #=> :bar
16
17
  class Proxy < BasicObject
17
18
  # @param receiver [Object]
18
19
  # @param method [Symbol]
19
- # @param preargs [Array]
20
- def initialize(receiver, method, *preargs)
20
+ def initialize(receiver, method)
21
21
  @receiver = receiver
22
22
  @method = method
23
- @preargs = preargs
24
23
  end
25
24
 
26
25
  # @!visibility private
27
26
  def respond_to_missing?(_method_name, _) = true
28
27
 
29
- def method_missing(method_name, ...)
30
- @receiver.__send__(@method, *@preargs, method_name, ...)
31
- end
28
+ def method_missing(method_name, ...) = @receiver.__send__(@method, method_name, ...)
32
29
  end
33
30
  end
34
31
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module FactoryBot
4
4
  class With
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
@@ -96,7 +96,7 @@ module FactoryBot
96
96
  # @param build_strategy [Symbol]
97
97
  # @param ancestors [Array<Array(AssocInfo, Object)>, nil]
98
98
  # @return [Object]
99
- def instantiate(build_strategy, ancestors = nil)
99
+ def instantiate(build_strategy, ancestors = self.class.scoped_ancestors)
100
100
  return self if build_strategy == :with
101
101
 
102
102
  factory_bot_method =
@@ -148,12 +148,40 @@ module FactoryBot
148
148
  list: :"#{build_strategy}_list",
149
149
  }.each do |variation, method_name|
150
150
  Methods.define_method(method_name) do |factory = nil, *args, **kwargs, &block|
151
- return Proxy.new(self, __method__) unless factory
152
-
153
- With.build(variation, factory, *args, **kwargs, &block).instantiate(build_strategy)
151
+ if factory
152
+ # <__method__>(<factory_name>, ...)
153
+ With.build(variation, factory, *args, **kwargs, &block).instantiate(build_strategy)
154
+ elsif args.empty? && kwargs.empty? && !block
155
+ # <__method__>.<factory_name>(...)
156
+ Proxy.new(self, __method__)
157
+ elsif __method__ == :with && args.empty? && !kwargs.empty?
158
+ # with(<factory_name>: <object>, ...) { ... }
159
+ With.with_scoped_ancestors(kwargs, &block)
160
+ else
161
+ raise ArgumentError, "Invalid use of #{__method__}"
162
+ end
154
163
  end
155
164
  end
156
165
  end
166
+
167
+ # @!visibility private
168
+ # @param objects [{Symbol => Object}]
169
+ def with_scoped_ancestors(objects)
170
+ return unless block_given?
171
+
172
+ tmp_scoped_ancestors = scoped_ancestors
173
+ Thread.current[:factory_bot_with_scoped_ancestors] = [
174
+ *objects.map { [AssocInfo.get(_1), _2] },
175
+ *tmp_scoped_ancestors || [],
176
+ ]
177
+ result = yield
178
+ Thread.current[:factory_bot_with_scoped_ancestors] = tmp_scoped_ancestors
179
+ result
180
+ end
181
+
182
+ # @!visibility private
183
+ # @return [Array<Array(AssocInfo, Object)>, nil]
184
+ def scoped_ancestors = Thread.current[:factory_bot_with_scoped_ancestors]
157
185
  end
158
186
 
159
187
  %i[build build_stubbed create attributes_for with].each { register_strategy _1 }
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: factory_bot-with
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - yubrot
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-12-09 00:00:00.000000000 Z
10
+ date: 2025-03-15 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: factory_bot
@@ -52,7 +51,6 @@ metadata:
52
51
  source_code_uri: https://github.com/yubrot/factory_bot-with
53
52
  changelog_uri: https://github.com/yubrot/factory_bot-with/blob/main/CHANGELOG.md
54
53
  rubygems_mfa_required: 'true'
55
- post_install_message:
56
54
  rdoc_options: []
57
55
  require_paths:
58
56
  - lib
@@ -67,8 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
65
  - !ruby/object:Gem::Version
68
66
  version: '0'
69
67
  requirements: []
70
- rubygems_version: 3.5.22
71
- signing_key:
68
+ rubygems_version: 3.6.2
72
69
  specification_version: 4
73
70
  summary: FactoryBot extension that wraps FactoryBot::Syntax::Methods to make it a
74
71
  little easier to use