factory_bot 5.0.0 → 5.1.2

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.
data/README.md CHANGED
@@ -5,7 +5,7 @@ factory_bot is a fixtures replacement with a straightforward definition syntax,
5
5
  If you want to use factory_bot with Rails, see
6
6
  [factory_bot_rails](https://github.com/thoughtbot/factory_bot_rails).
7
7
 
8
- _[Interested in the history of the project name?](NAME.md)_
8
+ _[Interested in the history of the project name?][NAME]_
9
9
 
10
10
 
11
11
  ### Transitioning from factory\_girl?
@@ -59,9 +59,13 @@ More Information
59
59
  * [Issues](https://github.com/thoughtbot/factory_bot/issues)
60
60
  * [GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS](https://robots.thoughtbot.com/)
61
61
 
62
- You may also find useful information under the [factory_girl tag on Stack Overflow](https://stackoverflow.com/questions/tagged/factory-girl).
62
+ [GETTING_STARTED]: https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md
63
+ [NAME]: https://github.com/thoughtbot/factory_bot/blob/master/NAME.md
63
64
 
64
- [GETTING_STARTED]: https://www.rubydoc.info/gems/factory_bot/file/GETTING_STARTED.md
65
+ Useful Tools
66
+ ------------
67
+
68
+ * [FactoryTrace](https://github.com/djezzzl/factory_trace) - helps to find unused factories and traits.
65
69
 
66
70
  Contributing
67
71
  ------------
@@ -75,14 +79,17 @@ community](https://github.com/thoughtbot/factory_bot/graphs/contributors).
75
79
  License
76
80
  -------
77
81
 
78
- factory_bot is Copyright © 2008-2016 Joe Ferris and thoughtbot. It is free
82
+ factory_bot is Copyright © 2008-2019 Joe Ferris and thoughtbot. It is free
79
83
  software, and may be redistributed under the terms specified in the
80
- [LICENSE](/LICENSE) file.
84
+ [LICENSE] file.
85
+
86
+ [LICENSE]: https://github.com/thoughtbot/factory_bot/blob/master/LICENSE
87
+
81
88
 
82
89
  About thoughtbot
83
90
  ----------------
84
91
 
85
- ![thoughtbot](https://presskit.thoughtbot.com/images/thoughtbot-logo-for-readmes.svg)
92
+ ![thoughtbot](https://thoughtbot.com/brand_assets/93:44.svg)
86
93
 
87
94
  factory_bot is maintained and funded by thoughtbot, inc.
88
95
  The names and logos for thoughtbot are trademarks of thoughtbot, inc.
@@ -45,16 +45,17 @@ require "factory_bot/decorator/invocation_tracker"
45
45
  require "factory_bot/decorator/new_constructor"
46
46
  require "factory_bot/linter"
47
47
  require "factory_bot/version"
48
+ require "factory_bot/internal"
48
49
 
49
50
  module FactoryBot
50
- DEPRECATOR = ActiveSupport::Deprecation.new("6.0", "factory_bot")
51
+ Deprecation = ActiveSupport::Deprecation.new("6.0", "factory_bot")
51
52
 
52
53
  def self.configuration
53
- @configuration ||= Configuration.new
54
+ Internal.configuration
54
55
  end
55
56
 
56
57
  def self.reset_configuration
57
- @configuration = nil
58
+ Internal.reset_configuration
58
59
  end
59
60
 
60
61
  mattr_accessor :use_parent_strategy, instance_accessor: false
@@ -70,92 +71,55 @@ module FactoryBot
70
71
  def self.lint(*args)
71
72
  options = args.extract_options!
72
73
  factories_to_lint = args[0] || FactoryBot.factories
73
- Linter.new(factories_to_lint, options).lint!
74
+ Linter.new(factories_to_lint, **options).lint!
74
75
  end
75
76
 
76
77
  class << self
77
- delegate :factories,
78
+ delegate :callbacks,
79
+ :callback_names,
80
+ :constructor,
81
+ :factories,
82
+ :initialize_with,
78
83
  :sequences,
79
- :traits,
80
- :callbacks,
84
+ :skip_create,
81
85
  :strategies,
82
- :callback_names,
83
86
  :to_create,
84
- :skip_create,
85
- :initialize_with,
86
- :constructor,
87
+ :traits,
87
88
  to: :configuration
88
89
 
89
- attr_accessor :allow_class_lookup
90
- deprecate :allow_class_lookup, :allow_class_lookup=, deprecator: DEPRECATOR
91
- end
92
-
93
- def self.register_factory(factory)
94
- factory.names.each do |name|
95
- factories.register(name, factory)
96
- end
97
- factory
98
- end
99
-
100
- def self.factory_by_name(name)
101
- factories.find(name)
102
- end
103
-
104
- def self.register_sequence(sequence)
105
- sequence.names.each do |name|
106
- sequences.register(name, sequence)
107
- end
108
- sequence
109
- end
110
-
111
- def self.sequence_by_name(name)
112
- sequences.find(name)
113
- end
114
-
115
- def self.rewind_sequences
116
- sequences.each(&:rewind)
117
- end
118
-
119
- def self.register_trait(trait)
120
- trait.names.each do |name|
121
- traits.register(name, trait)
122
- end
123
- trait
124
- end
125
-
126
- def self.trait_by_name(name)
127
- traits.find(name)
128
- end
90
+ delegate :factory_by_name,
91
+ :register_callback,
92
+ :register_default_callbacks,
93
+ :register_default_strategies,
94
+ :register_factory,
95
+ :register_sequence,
96
+ :register_strategy,
97
+ :register_trait,
98
+ :rewind_sequences,
99
+ :sequence_by_name,
100
+ :strategy_by_name,
101
+ :trait_by_name,
102
+ to: Internal
129
103
 
130
- def self.register_strategy(strategy_name, strategy_class)
131
- strategies.register(strategy_name, strategy_class)
132
- StrategySyntaxMethodRegistrar.new(strategy_name).define_strategy_methods
133
- end
134
-
135
- def self.strategy_by_name(name)
136
- strategies.find(name)
137
- end
138
-
139
- def self.register_default_strategies
140
- register_strategy(:build, FactoryBot::Strategy::Build)
141
- register_strategy(:create, FactoryBot::Strategy::Create)
142
- register_strategy(:attributes_for, FactoryBot::Strategy::AttributesFor)
143
- register_strategy(:build_stubbed, FactoryBot::Strategy::Stub)
144
- register_strategy(:null, FactoryBot::Strategy::Null)
145
- end
146
-
147
- def self.register_default_callbacks
148
- register_callback(:after_build)
149
- register_callback(:after_create)
150
- register_callback(:after_stub)
151
- register_callback(:before_create)
152
- end
104
+ attr_accessor :allow_class_lookup
153
105
 
154
- def self.register_callback(name)
155
- name = name.to_sym
156
- callback_names << name
106
+ deprecate :allow_class_lookup,
107
+ :allow_class_lookup=,
108
+ :callback_names,
109
+ :factory_by_name,
110
+ :register_callback,
111
+ :register_default_callbacks,
112
+ :register_default_strategies,
113
+ :register_factory,
114
+ :register_trait,
115
+ :sequence_by_name,
116
+ :sequences,
117
+ :strategies,
118
+ :trait_by_name,
119
+ :traits,
120
+ deprecator: Deprecation
157
121
  end
158
122
  end
159
123
 
160
- FactoryBot.register_default_strategies
161
- FactoryBot.register_default_callbacks
124
+ FactoryBot::Internal.register_default_strategies
125
+ FactoryBot::Internal.register_default_callbacks
@@ -16,6 +16,7 @@ module FactoryBot
16
16
  else instance_exec(&block)
17
17
  end
18
18
  raise SequenceAbuseError if FactoryBot::Sequence === value
19
+
19
20
  value
20
21
  }
21
22
  end
@@ -2,7 +2,7 @@ module FactoryBot
2
2
  # @api private
3
3
  class AttributeAssigner
4
4
  def initialize(evaluator, build_class, &instance_builder)
5
- @build_class = build_class
5
+ @build_class = build_class
6
6
  @instance_builder = instance_builder
7
7
  @evaluator = evaluator
8
8
  @attribute_list = evaluator.class.attribute_list
@@ -28,9 +28,9 @@ module FactoryBot
28
28
  private
29
29
 
30
30
  def ensure_valid_callback_name!
31
- unless FactoryBot.callback_names.include?(name)
31
+ unless FactoryBot::Internal.callback_names.include?(name)
32
32
  raise InvalidCallbackNameError, "#{name} is not a valid callback name. " +
33
- "Valid callback names are #{FactoryBot.callback_names.inspect}"
33
+ "Valid callback names are #{FactoryBot::Internal.callback_names.inspect}"
34
34
  end
35
35
  end
36
36
 
@@ -1,7 +1,14 @@
1
1
  module FactoryBot
2
2
  # @api private
3
3
  class Configuration
4
- attr_reader :factories, :sequences, :traits, :strategies, :callback_names
4
+ attr_reader(
5
+ :callback_names,
6
+ :factories,
7
+ :inline_sequences,
8
+ :sequences,
9
+ :strategies,
10
+ :traits,
11
+ )
5
12
 
6
13
  def initialize
7
14
  @factories = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Factory"))
@@ -10,13 +17,14 @@ module FactoryBot
10
17
  @strategies = Registry.new("Strategy")
11
18
  @callback_names = Set.new
12
19
  @definition = Definition.new(:configuration)
20
+ @inline_sequences = []
13
21
 
14
22
  to_create(&:save!)
15
23
  initialize_with { new }
16
24
  end
17
25
 
18
26
  delegate :to_create, :skip_create, :constructor, :before, :after,
19
- :callback, :callbacks, to: :@definition
27
+ :callback, :callbacks, to: :@definition
20
28
 
21
29
  def initialize_with(&block)
22
30
  @definition.define_constructor(&block)
@@ -22,9 +22,20 @@ module FactoryBot
22
22
  private
23
23
 
24
24
  def build
25
+ ensure_factory_is_not_a_declaration!
26
+
25
27
  factory_name = @overrides[:factory] || name
26
28
  [Attribute::Association.new(name, factory_name, [@traits, @overrides.except(:factory)].flatten)]
27
29
  end
30
+
31
+ def ensure_factory_is_not_a_declaration!
32
+ if @overrides[:factory].is_a?(Declaration)
33
+ raise ArgumentError.new(<<~MSG)
34
+ Association '#{name}' received an invalid factory argument.
35
+ Did you mean? 'factory: :#{@overrides[:factory].name}'
36
+ MSG
37
+ end
38
+ end
28
39
  end
29
40
  end
30
41
  end
@@ -23,8 +23,11 @@ module FactoryBot
23
23
  def build
24
24
  if FactoryBot.factories.registered?(name)
25
25
  [Attribute::Association.new(name, name, {})]
26
- elsif FactoryBot.sequences.registered?(name)
26
+ elsif FactoryBot::Internal.sequences.registered?(name)
27
27
  [Attribute::Sequence.new(name, name, @ignored)]
28
+ elsif @factory.name.to_s == name.to_s
29
+ message = "Self-referencing trait '#{@name}'"
30
+ raise TraitDefinitionError, message
28
31
  else
29
32
  @factory.inherit_traits([name])
30
33
  []
@@ -6,7 +6,7 @@ module FactoryBot
6
6
  @component = component
7
7
  end
8
8
 
9
- def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissing
9
+ def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissingSuper
10
10
  @component.send(name, *args, &block)
11
11
  end
12
12
 
@@ -6,7 +6,7 @@ module FactoryBot
6
6
  @invoked_methods = []
7
7
  end
8
8
 
9
- def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissing
9
+ def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing
10
10
  @invoked_methods << name
11
11
  super
12
12
  end
@@ -49,7 +49,7 @@ module FactoryBot
49
49
 
50
50
  defined_traits.each do |defined_trait|
51
51
  base_traits.each { |bt| bt.define_trait defined_trait }
52
- additional_traits.each { |bt| bt.define_trait defined_trait }
52
+ additional_traits.each { |at| at.define_trait defined_trait }
53
53
  end
54
54
 
55
55
  @compiled = true
@@ -95,7 +95,7 @@ module FactoryBot
95
95
 
96
96
  def callback(*names, &block)
97
97
  names.each do |name|
98
- FactoryBot.register_callback(name)
98
+ FactoryBot::Internal.register_callback(name)
99
99
  add_callback(Callback.new(name, block))
100
100
  end
101
101
  end
@@ -111,17 +111,19 @@ module FactoryBot
111
111
  end
112
112
 
113
113
  def trait_by_name(name)
114
- trait_for(name) || FactoryBot.trait_by_name(name)
114
+ trait_for(name) || Internal.trait_by_name(name)
115
115
  end
116
116
 
117
117
  def trait_for(name)
118
- defined_traits.detect { |trait| trait.name == name }
118
+ @defined_traits_by_name ||= defined_traits.each_with_object({}) { |t, memo| memo[t.name] ||= t }
119
+ @defined_traits_by_name[name.to_s]
119
120
  end
120
121
 
121
122
  def initialize_copy(source)
122
123
  super
123
124
  @attributes = nil
124
125
  @compiled = false
126
+ @defined_traits_by_name = nil
125
127
  end
126
128
 
127
129
  def aggregate_from_traits_and_self(method_name, &block)
@@ -88,15 +88,18 @@ module FactoryBot
88
88
  # end
89
89
  #
90
90
  # are equivalent.
91
- def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissing
92
- if args.empty?
91
+ def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing, Style/MethodMissingSuper, Metrics/LineLength
92
+ association_options = args.first
93
+
94
+ if association_options.nil?
93
95
  __declare_attribute__(name, block)
94
- elsif args.first.respond_to?(:has_key?) && args.first.has_key?(:factory)
95
- association(name, *args)
96
+ elsif __valid_association_options?(association_options)
97
+ association(name, association_options)
96
98
  else
97
- raise NoMethodError.new(
98
- "undefined method '#{name}' in '#{@definition.name}' factory",
99
- )
99
+ raise NoMethodError.new(<<~MSG)
100
+ undefined method '#{name}' in '#{@definition.name}' factory
101
+ Did you mean? '#{name} { #{association_options.inspect} }'
102
+ MSG
100
103
  end
101
104
  end
102
105
 
@@ -117,9 +120,8 @@ module FactoryBot
117
120
  #
118
121
  # Except that no globally available sequence will be defined.
119
122
  def sequence(name, *args, &block)
120
- sequence_name = "__#{@definition.name}_#{name}__"
121
- sequence = Sequence.new(sequence_name, *args, &block)
122
- FactoryBot.register_sequence(sequence)
123
+ sequence = Sequence.new(name, *args, &block)
124
+ FactoryBot::Internal.register_inline_sequence(sequence)
123
125
  add_attribute(name) { increment_sequence(sequence) }
124
126
  end
125
127
 
@@ -188,5 +190,9 @@ module FactoryBot
188
190
  add_attribute(name, &block)
189
191
  end
190
192
  end
193
+
194
+ def __valid_association_options?(options)
195
+ options.respond_to?(:has_key?) && options.has_key?(:factory)
196
+ end
191
197
  end
192
198
  end
@@ -2,6 +2,9 @@ module FactoryBot
2
2
  # Raised when a factory is defined that attempts to instantiate itself.
3
3
  class AssociationDefinitionError < RuntimeError; end
4
4
 
5
+ # Raised when a trait is defined that references itself.
6
+ class TraitDefinitionError < RuntimeError; end
7
+
5
8
  # Raised when a callback is defined that has an invalid name
6
9
  class InvalidCallbackNameError < RuntimeError; end
7
10
 
@@ -37,7 +37,7 @@ module FactoryBot
37
37
  @instance = object_instance
38
38
  end
39
39
 
40
- def method_missing(method_name, *args, &block) # rubocop:disable Style/MethodMissing
40
+ def method_missing(method_name, *args, &block) # rubocop:disable Style/MethodMissingSuper
41
41
  if @instance.respond_to?(method_name)
42
42
  @instance.send(method_name, *args, &block)
43
43
  else
@@ -66,7 +66,7 @@ module FactoryBot
66
66
  end
67
67
 
68
68
  def self.define_attribute(name, &block)
69
- if method_defined?(name) || private_method_defined?(name)
69
+ if instance_methods(false).include?(name) || private_instance_methods(false).include?(name)
70
70
  undef_method(name)
71
71
  end
72
72
 
@@ -145,7 +145,7 @@ module FactoryBot
145
145
 
146
146
  def parent
147
147
  if @parent
148
- FactoryBot.factory_by_name(@parent)
148
+ FactoryBot::Internal.factory_by_name(@parent)
149
149
  else
150
150
  NullFactory.new
151
151
  end
@@ -5,11 +5,11 @@ module FactoryBot
5
5
  @strategy = strategy
6
6
 
7
7
  @overrides = traits_and_overrides.extract_options!
8
- @traits = traits_and_overrides.map(&:to_sym)
8
+ @traits = traits_and_overrides
9
9
  end
10
10
 
11
11
  def run(runner_strategy = @strategy, &block)
12
- factory = FactoryBot.factory_by_name(@name)
12
+ factory = FactoryBot::Internal.factory_by_name(@name)
13
13
 
14
14
  factory.compile
15
15