factory_bot-with 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a279f1170ba696ac48614f1c95572bf4be5a9b3f5ac5e47a3574813deafd5623
4
- data.tar.gz: dddb6e8f841108fd70a80d509ea847a9daf6b0613111bf9977c8b79a6e32bf20
3
+ metadata.gz: 01b5be917f74ffbdc5643be7bb5f9c9e7f1009484f688a8442aed6dda8dc5a0b
4
+ data.tar.gz: d773865f81b4aad3e87dea573db938b5393f0ecc988b5810b827741051ee6864
5
5
  SHA512:
6
- metadata.gz: 2dc2904bb72bee299282e42f9daa0ba697e7c4eb7febd3475697ff0db2df89c03aa71c5bc55e2e97d2a786a3f3449b6666d68b643874a70bc32a5f6ef0a379ee
7
- data.tar.gz: 3b322dbad72231b9e16e97113a3a300db5a88a90d2a67c06ef026711881c2de52f48ca0326810be0ab3e1ce7f57ca56d238a1070d6a78eca2ddf5d667dd226ea
6
+ metadata.gz: 93c900afe0c21747d9837a5c8e1b31b11a7ae3e127873cdf86f43f5f83fac4ba10be0295e7b43542d3399a78be377fd240f57a9251a8433471015767170d96a5
7
+ data.tar.gz: 1af4669b1afb19783332b2bed70d7cb76d210bb205766dcc3aae06bd07de0a4a614da1aadc0841e6cca0bf8c3b3965241a05703a898172a0c575ddcd93f8d273
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2024-11-29
4
+
5
+ - Fixed: Fixed an incorrect factory resolution problem after factory names autocompletion
6
+ - Added: Smarter interpretation of positional arguments passed to factory methods
7
+
3
8
  ## [0.1.0] - 2024-11-27
4
9
 
5
10
  - Initial release
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # FactoryBot::With
2
2
 
3
+ [![](https://badge.fury.io/rb/factory_bot-with.svg)](https://badge.fury.io/rb/factory_bot-with)
4
+ [![](https://github.com/yubrot/factory_bot-with/actions/workflows/main.yml/badge.svg)](https://github.com/yubrot/factory_bot-with/actions/workflows/main.yml)
5
+
3
6
  FactoryBot::With is a FactoryBot extension that wraps `FactoryBot::Syntax::Methods` to make it a little easier to use.
4
7
 
5
8
  For example, with these factories:
@@ -68,6 +71,16 @@ build_stubbed.foo(...)
68
71
  create_list.foo(10, ...)
69
72
  ```
70
73
 
74
+ ### Smarter interpretation of positional arguments
75
+
76
+ FactoryBot::With adjusts the behavior of factory methods to accept `Hash`, `Array`, and falsy values (`false` or `nil`) as positional arguments.
77
+
78
+ ```ruby
79
+ create.foo({ title: "Recipe" }, is_new && %i[latest hot])
80
+ #=> create(:foo, :latest, :hot, title: "Recipe") if is_new
81
+ # create(:foo, title: "Recipe") otherwise
82
+ ```
83
+
71
84
  ### `with`, `with_pair`, and `with_list` operator
72
85
 
73
86
  FactoryBot::With provides a new operator `with` (and its family).
@@ -18,14 +18,7 @@ module FactoryBot
18
18
  define_method(method_name) do |factory = nil, *args, **kwargs, &block|
19
19
  return Proxy.new(self, __method__) unless factory
20
20
 
21
- with =
22
- if factory.is_a? With
23
- factory.merge(With.new(variation, factory.factory_name, *args, **kwargs, &block))
24
- else
25
- With.new(variation, factory, *args, **kwargs, &block)
26
- end
27
-
28
- with.instantiate(build_strategy)
21
+ With.build(variation, factory, *args, **kwargs, &block).instantiate(build_strategy)
29
22
  end
30
23
  end
31
24
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module FactoryBot
4
4
  class With
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.0"
6
6
  end
7
7
  end
@@ -15,25 +15,32 @@ module FactoryBot
15
15
  attr_reader :factory_name
16
16
  # @return [Array<With>]
17
17
  attr_reader :withes
18
- # @return [Array<Object>]
19
- attr_reader :args
18
+ # @return [Array<Numeric, Symbol>] Numeric is also treated as a trait for convenience
19
+ attr_reader :traits
20
20
  # @return [{Symbol => Object}]
21
- attr_reader :kwargs
21
+ attr_reader :attrs
22
22
  # @return [Proc, nil]
23
23
  attr_reader :block
24
24
 
25
25
  # @!visibility private
26
- def initialize(variation, factory_name, *args, **kwargs, &block)
26
+ def initialize(variation, factory_name, withes: [], traits: [], attrs: {}, &block)
27
+ raise ArgumentError unless %i[unit pair list].include? variation
28
+ raise TypeError unless factory_name.is_a? Symbol
29
+ raise TypeError unless withes.is_a?(Array) && withes.all? { _1.is_a? self.class }
30
+ raise TypeError unless traits.is_a?(Array) && traits.all? { _1.is_a?(Symbol) || _1.is_a?(Numeric) }
31
+ raise TypeError unless attrs.is_a?(Hash) && attrs.keys.all? { _1.is_a? Symbol }
32
+
27
33
  @variation = variation
28
34
  @factory_name = factory_name
29
- @withes, @args = args.partition { _1.is_a? self.class }
30
- @kwargs = kwargs
35
+ @withes = withes
36
+ @traits = traits
37
+ @attrs = attrs
31
38
  @block = block
32
39
  end
33
40
 
34
41
  # @!visibility private
35
42
  # @return [Boolean]
36
- def plain? = withes.empty? && args.empty? && kwargs.empty? && block.nil?
43
+ def plain? = withes.empty? && traits.empty? && attrs.empty? && block.nil?
37
44
 
38
45
  # @!visibility private
39
46
  # @param other [With]
@@ -49,8 +56,9 @@ module FactoryBot
49
56
  self.class.new(
50
57
  variation,
51
58
  factory_name,
52
- *args, *other.args, *withes, *other.withes,
53
- **kwargs, **other.kwargs,
59
+ withes: [*withes, *other.withes],
60
+ traits: [*traits, *other.traits],
61
+ attrs: { **attrs, **other.attrs },
54
62
  &self.class.merge_block(block, other.block)
55
63
  )
56
64
  end
@@ -63,20 +71,20 @@ module FactoryBot
63
71
  return self if build_strategy == :with
64
72
 
65
73
  factory_bot_method = Methods::VARIATIONS[variation][build_strategy]
66
- factory_name, kwargs =
74
+ factory_name, attrs =
67
75
  if ancestors
68
- kwargs = @kwargs.dup
76
+ attrs = @attrs.dup
69
77
  factory_name = AssocInfo.autocomplete_fully_qualified_factory_name(ancestors, @factory_name)
70
- AssocInfo.get(factory_name).perform_automatic_association_resolution(ancestors, kwargs)
71
- [factory_name, kwargs]
78
+ AssocInfo.get(factory_name).perform_automatic_association_resolution(ancestors, attrs)
79
+ [factory_name, attrs]
72
80
  else
73
- [@factory_name, @kwargs]
81
+ [@factory_name, @attrs]
74
82
  end
75
- result = FactoryBot.__send__(factory_bot_method, factory_name, *args, **kwargs, &block)
83
+ result = FactoryBot.__send__(factory_bot_method, factory_name, *traits, **attrs, &block)
76
84
 
77
85
  unless withes.empty?
78
86
  parents = variation == :unit ? [result] : result
79
- assoc_info = AssocInfo.get(@factory_name)
87
+ assoc_info = AssocInfo.get(factory_name)
80
88
  parents.each do |parent|
81
89
  ancestors_for_children = [[assoc_info, parent], *ancestors || []]
82
90
  withes.each { _1.instantiate(build_strategy, ancestors_for_children) }
@@ -86,17 +94,52 @@ module FactoryBot
86
94
  result
87
95
  end
88
96
 
89
- # @!visibility private
90
- # @param first [Proc, nil]
91
- # @param second [Proc, nil]
92
- # @return [Proc, nil]
93
- def self.merge_block(first, second)
94
- return first unless second
95
- return second unless first
97
+ class << self
98
+ # @!visibility private
99
+ # @param variation [:unit, :pair, :list]
100
+ # @param factory [Symbol, With]
101
+ # @param args [Array<Object>]
102
+ # @param kwargs [{Symbol => Object}]
103
+ def build(variation, factory, *, **, &)
104
+ return factory.merge(build(variation, factory.factory_name, *, **, &)) if factory.is_a? self
105
+
106
+ with = new(variation, factory, &)
107
+ insert_args!(with, *)
108
+ with.attrs.merge!(**)
109
+ with
110
+ end
96
111
 
97
- lambda do |*args|
98
- first.call(*args)
99
- second.call(*args)
112
+ def insert_args!(with, *args)
113
+ args.each do |arg|
114
+ case arg
115
+ when self
116
+ with.withes << arg
117
+ when Symbol, Numeric
118
+ with.traits << arg
119
+ when Array
120
+ insert_args!(with, *arg)
121
+ when Hash
122
+ with.attrs.merge!(arg)
123
+ when false, nil
124
+ # Ignored. This behavior is useful for conditional arguments like `is_premium && :premium`
125
+ else
126
+ raise ArgumentError, "Unsupported type for factory argument: #{arg}"
127
+ end
128
+ end
129
+ end
130
+
131
+ # @!visibility private
132
+ # @param first [Proc, nil]
133
+ # @param second [Proc, nil]
134
+ # @return [Proc, nil]
135
+ def merge_block(first, second)
136
+ return first unless second
137
+ return second unless first
138
+
139
+ lambda do |*args|
140
+ first.call(*args)
141
+ second.call(*args)
142
+ end
100
143
  end
101
144
  end
102
145
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: factory_bot-with
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - yubrot
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-27 00:00:00.000000000 Z
11
+ date: 2024-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: factory_bot
@@ -67,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  requirements: []
70
- rubygems_version: 3.5.23
70
+ rubygems_version: 3.5.22
71
71
  signing_key:
72
72
  specification_version: 4
73
73
  summary: FactoryBot extension that wraps FactoryBot::Syntax::Methods to make it a