factory_bot 5.0.1 → 6.1.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +52 -13
  3. data/GETTING_STARTED.md +453 -78
  4. data/NEWS.md +44 -1
  5. data/README.md +17 -16
  6. data/lib/factory_bot.rb +21 -93
  7. data/lib/factory_bot/aliases.rb +3 -3
  8. data/lib/factory_bot/attribute/association.rb +2 -2
  9. data/lib/factory_bot/attribute/dynamic.rb +2 -1
  10. data/lib/factory_bot/attribute_assigner.rb +8 -9
  11. data/lib/factory_bot/attribute_list.rb +1 -1
  12. data/lib/factory_bot/callback.rb +2 -10
  13. data/lib/factory_bot/configuration.rb +6 -6
  14. data/lib/factory_bot/declaration.rb +1 -1
  15. data/lib/factory_bot/declaration/association.rb +30 -2
  16. data/lib/factory_bot/declaration/implicit.rb +4 -1
  17. data/lib/factory_bot/declaration_list.rb +2 -2
  18. data/lib/factory_bot/decorator.rb +18 -6
  19. data/lib/factory_bot/decorator/invocation_tracker.rb +10 -3
  20. data/lib/factory_bot/definition.rb +51 -18
  21. data/lib/factory_bot/definition_hierarchy.rb +1 -11
  22. data/lib/factory_bot/definition_proxy.rb +77 -12
  23. data/lib/factory_bot/enum.rb +27 -0
  24. data/lib/factory_bot/errors.rb +3 -0
  25. data/lib/factory_bot/evaluator.rb +20 -12
  26. data/lib/factory_bot/evaluator_class_definer.rb +1 -1
  27. data/lib/factory_bot/factory.rb +13 -13
  28. data/lib/factory_bot/factory_runner.rb +4 -4
  29. data/lib/factory_bot/find_definitions.rb +1 -1
  30. data/lib/factory_bot/internal.rb +68 -1
  31. data/lib/factory_bot/linter.rb +9 -13
  32. data/lib/factory_bot/null_factory.rb +10 -4
  33. data/lib/factory_bot/null_object.rb +2 -6
  34. data/lib/factory_bot/registry.rb +4 -4
  35. data/lib/factory_bot/reload.rb +1 -2
  36. data/lib/factory_bot/sequence.rb +5 -5
  37. data/lib/factory_bot/strategy/null.rb +4 -2
  38. data/lib/factory_bot/strategy/stub.rb +16 -5
  39. data/lib/factory_bot/strategy_calculator.rb +1 -1
  40. data/lib/factory_bot/strategy_syntax_method_registrar.rb +12 -1
  41. data/lib/factory_bot/syntax/default.rb +11 -23
  42. data/lib/factory_bot/syntax/methods.rb +3 -3
  43. data/lib/factory_bot/trait.rb +6 -4
  44. data/lib/factory_bot/version.rb +1 -1
  45. metadata +9 -37
data/NEWS.md CHANGED
@@ -1,10 +1,53 @@
1
1
  # News
2
2
 
3
+ ## 6.1.0 (July 8, 2020)
4
+ * Added: public reader for the evaluation instance, helpful for building interrelated associations
5
+ * Changed: raise a more helpful error when passing an invalid argument to an association
6
+ * Fixed: Ruby 2.7 kwarg deprecation warnings
7
+
8
+ ## 6.0.2 (June 19, 2020)
9
+ * Fixed: bug causing traits to consume more memory each time they were used
10
+
11
+ ## 6.0.1 (June 19, 2020)
12
+ * Fixed: bug with constant resolution causing unexpected uninitialized constant errors
13
+
14
+ ## 6.0.0 (June 18, 2020)
15
+ * Added: automatic definition of traits for Active Record enum attributes, enabled by default
16
+ (Note that this required changing where factory_bot constantizes the build
17
+ class, which may affect applications that were using abstract factories for
18
+ inheritance. See issue #1409.)
19
+ * Added: `traits_for_enum` method to define traits for non-Active Record enums
20
+ * Added: `build_stubbed_starting_id=` option to define the starting id for `build_stubbed`
21
+ * Removed: deprecated methods on the top-level `FactoryBot` module meant only for internal use
22
+ * Removed: support for EOL versions of Ruby (2.3, 2.4) and Rails (4.2)
23
+
24
+ ## 5.2.0 (April 24, 2020)
25
+ * Added: Pass index to block for `*_list` methods
26
+ * Deprecated: methods on the top-level `FactoryBot` module meant only for internal use: `callbacks`, `configuration`, `constructor`, `initialize_with`, `register_sequence`, `resent_configuration`, `skip_create`, `to_create`
27
+
28
+ ## 5.1.2 (March 25, 2020)
29
+ * Fixed: Ruby 2.7 keyword deprecation warning in FactoryBot.lint
30
+
31
+ ## 5.1.1 (October 2, 2019)
32
+ * Improved: performance of traits
33
+ * Fixed: registering strategies on JRuby
34
+
35
+ ## 5.1.0 (September 21, 2019)
36
+ * Added: "Did you mean?" style error message to help with typos in association declarations
37
+ * Changed: `NoMethodError` for static attributes now offers a "Did you mean?" style message
38
+ * Fixed: avoid undefining inherited evaluator methods
39
+ * Fixed: avoid stubbing id for records without a primary key
40
+ * Fixed: raise a helpful error for self-referencing traits to avoid a `SystemStackError`
41
+ * Deprecated: methods on the top-level `FactoryBot` module meant only for internal use: `allow_class_lookup`, `allow_class_lookup`=, `register_trait`, `trait_by_name`, `traits`, `sequence_by_name`, `sequences`, `factory_by_name`, `register_factory`, `callback_names`, `register_callback`, `register_default_callbacks`, `register_default_strategies`, `strategies`
42
+
43
+ ## 5.0.2 (February 22, 2019)
44
+ * Bugfix: raise "Trait not registered" error when passing invalid trait arguments
45
+
3
46
  ## 5.0.1 (February 15, 2019)
4
47
  * Bugfix: Do not raise error when two sequences have the same name
5
48
  in two traits that have the same name
6
49
 
7
- ## 5.0.0
50
+ ## 5.0.0 (February 1, 2019)
8
51
  * Added: Verbose option to include full backtraces in the linting output
9
52
  * Changed: use_parent_strategy now defaults to true, so by default the
10
53
  build strategy will build, rather than create associations
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
- # factory_bot [![Build Status][ci-image]][ci] [![Code Climate][grade-image]][grade] [![Gem Version][version-image]][version] [![Reviewed by Hound][hound-badge-image]][hound]
1
+ # factory_bot [![Build Status][ci-image]][ci] [![Code Climate][grade-image]][grade] [![Gem Version][version-image]][version]
2
2
 
3
3
  factory_bot is a fixtures replacement with a straightforward definition syntax, support for multiple build strategies (saved instances, unsaved instances, attribute hashes, and stubbed objects), and support for multiple factories for the same class (user, admin_user, and so on), including factory inheritance.
4
4
 
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?
@@ -43,13 +43,7 @@ gem install factory_bot
43
43
  Supported Ruby versions
44
44
  -----------------------
45
45
 
46
- The factory_bot 5.x series supports MRI Ruby 2.3+.
47
-
48
- The factory_bot 3.x+ series supports MRI Ruby 1.9. Additionally, factory_bot
49
- 3.6+ supports JRuby 1.6.7.2+ while running in 1.9 mode. See [GETTING_STARTED]
50
- for more information on configuring the JRuby environment.
51
-
52
- For versions of Ruby prior to 1.9, please use factory_bot 2.x.
46
+ Supported Ruby versions are listed in [`.travis.yml`](https://github.com/thoughtbot/factory_bot/blob/master/.travis.yml)
53
47
 
54
48
  More Information
55
49
  ----------------
@@ -59,30 +53,37 @@ More Information
59
53
  * [Issues](https://github.com/thoughtbot/factory_bot/issues)
60
54
  * [GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS](https://robots.thoughtbot.com/)
61
55
 
62
- You may also find useful information under the [factory_girl tag on Stack Overflow](https://stackoverflow.com/questions/tagged/factory-girl).
56
+ [GETTING_STARTED]: https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md
57
+ [NAME]: https://github.com/thoughtbot/factory_bot/blob/master/NAME.md
58
+
59
+ Useful Tools
60
+ ------------
63
61
 
64
- [GETTING_STARTED]: https://www.rubydoc.info/gems/factory_bot/file/GETTING_STARTED.md
62
+ * [FactoryTrace](https://github.com/djezzzl/factory_trace) - helps to find unused factories and traits.
65
63
 
66
64
  Contributing
67
65
  ------------
68
66
 
69
67
  Please see [CONTRIBUTING.md](https://github.com/thoughtbot/factory_bot/blob/master/CONTRIBUTING.md).
70
68
 
71
- factory_bot was originally written by Joe Ferris and is now maintained by Josh
72
- Clayton. Many improvements and bugfixes were contributed by the [open source
69
+ factory_bot was originally written by Joe Ferris and is maintained by thoughtbot.
70
+ Many improvements and bugfixes were contributed by the [open source
73
71
  community](https://github.com/thoughtbot/factory_bot/graphs/contributors).
74
72
 
75
73
  License
76
74
  -------
77
75
 
78
- factory_bot is Copyright © 2008-2019 Joe Ferris and thoughtbot. It is free
76
+ factory_bot is Copyright © 2008-2020 Joe Ferris and thoughtbot. It is free
79
77
  software, and may be redistributed under the terms specified in the
80
- [LICENSE](/LICENSE) file.
78
+ [LICENSE] file.
79
+
80
+ [LICENSE]: https://github.com/thoughtbot/factory_bot/blob/master/LICENSE
81
+
81
82
 
82
83
  About thoughtbot
83
84
  ----------------
84
85
 
85
- ![thoughtbot](https://presskit.thoughtbot.com/images/thoughtbot-logo-for-readmes.svg)
86
+ ![thoughtbot](https://thoughtbot.com/brand_assets/93:44.svg)
86
87
 
87
88
  factory_bot is maintained and funded by thoughtbot, inc.
88
89
  The names and logos for thoughtbot are trademarks of thoughtbot, inc.
data/lib/factory_bot.rb CHANGED
@@ -4,6 +4,7 @@ require "active_support/core_ext/module/attribute_accessors"
4
4
  require "active_support/deprecation"
5
5
  require "active_support/notifications"
6
6
 
7
+ require "factory_bot/internal"
7
8
  require "factory_bot/definition_hierarchy"
8
9
  require "factory_bot/configuration"
9
10
  require "factory_bot/errors"
@@ -31,6 +32,7 @@ require "factory_bot/declaration"
31
32
  require "factory_bot/sequence"
32
33
  require "factory_bot/attribute_list"
33
34
  require "factory_bot/trait"
35
+ require "factory_bot/enum"
34
36
  require "factory_bot/aliases"
35
37
  require "factory_bot/definition"
36
38
  require "factory_bot/definition_proxy"
@@ -45,22 +47,16 @@ require "factory_bot/decorator/invocation_tracker"
45
47
  require "factory_bot/decorator/new_constructor"
46
48
  require "factory_bot/linter"
47
49
  require "factory_bot/version"
48
- require "factory_bot/internal"
49
50
 
50
51
  module FactoryBot
51
- DEPRECATOR = ActiveSupport::Deprecation.new("6.0", "factory_bot")
52
-
53
- def self.configuration
54
- Internal.configuration
55
- end
56
-
57
- def self.reset_configuration
58
- Internal.reset_configuration
59
- end
52
+ Deprecation = ActiveSupport::Deprecation.new("7.0", "factory_bot")
60
53
 
61
54
  mattr_accessor :use_parent_strategy, instance_accessor: false
62
55
  self.use_parent_strategy = true
63
56
 
57
+ mattr_accessor :automatically_define_enum_traits, instance_accessor: false
58
+ self.automatically_define_enum_traits = true
59
+
64
60
  # Look for errors in factories and (optionally) their traits.
65
61
  # Parameters:
66
62
  # factories - which factories to lint; omit for all factories
@@ -71,93 +67,25 @@ module FactoryBot
71
67
  def self.lint(*args)
72
68
  options = args.extract_options!
73
69
  factories_to_lint = args[0] || FactoryBot.factories
74
- Linter.new(factories_to_lint, options).lint!
75
- end
76
-
77
- class << self
78
- delegate :factories,
79
- :sequences,
80
- :traits,
81
- :callbacks,
82
- :strategies,
83
- :callback_names,
84
- :to_create,
85
- :skip_create,
86
- :initialize_with,
87
- :constructor,
88
- to: :configuration
89
-
90
- attr_accessor :allow_class_lookup
91
- deprecate :allow_class_lookup, :allow_class_lookup=, deprecator: DEPRECATOR
92
- end
93
-
94
- def self.register_factory(factory)
95
- factory.names.each do |name|
96
- factories.register(name, factory)
97
- end
98
- factory
99
- end
100
-
101
- def self.factory_by_name(name)
102
- factories.find(name)
103
- end
104
-
105
- def self.register_sequence(sequence)
106
- sequence.names.each do |name|
107
- sequences.register(name, sequence)
108
- end
109
- sequence
110
- end
111
-
112
- def self.sequence_by_name(name)
113
- sequences.find(name)
114
- end
115
-
116
- def self.rewind_sequences
117
- sequences.each(&:rewind)
118
- Internal.rewind_inline_sequences
119
- end
120
-
121
- def self.register_trait(trait)
122
- trait.names.each do |name|
123
- traits.register(name, trait)
124
- end
125
- trait
126
- end
127
-
128
- def self.trait_by_name(name)
129
- traits.find(name)
70
+ Linter.new(factories_to_lint, **options).lint!
130
71
  end
131
72
 
132
- def self.register_strategy(strategy_name, strategy_class)
133
- strategies.register(strategy_name, strategy_class)
134
- StrategySyntaxMethodRegistrar.new(strategy_name).define_strategy_methods
73
+ # Set the starting value for ids when using the build_stubbed strategy
74
+ #
75
+ # Arguments:
76
+ # * starting_id +Integer+
77
+ # The new starting id value.
78
+ def self.build_stubbed_starting_id=(starting_id)
79
+ Strategy::Stub.next_id = starting_id - 1
135
80
  end
136
81
 
137
- def self.strategy_by_name(name)
138
- strategies.find(name)
139
- end
140
-
141
- def self.register_default_strategies
142
- register_strategy(:build, FactoryBot::Strategy::Build)
143
- register_strategy(:create, FactoryBot::Strategy::Create)
144
- register_strategy(:attributes_for, FactoryBot::Strategy::AttributesFor)
145
- register_strategy(:build_stubbed, FactoryBot::Strategy::Stub)
146
- register_strategy(:null, FactoryBot::Strategy::Null)
147
- end
148
-
149
- def self.register_default_callbacks
150
- register_callback(:after_build)
151
- register_callback(:after_create)
152
- register_callback(:after_stub)
153
- register_callback(:before_create)
154
- end
155
-
156
- def self.register_callback(name)
157
- name = name.to_sym
158
- callback_names << name
82
+ class << self
83
+ delegate :factories,
84
+ :register_strategy,
85
+ :rewind_sequences,
86
+ :strategy_by_name,
87
+ to: Internal
159
88
  end
160
89
  end
161
90
 
162
- FactoryBot.register_default_strategies
163
- FactoryBot.register_default_callbacks
91
+ FactoryBot::Internal.register_default_strategies
@@ -5,14 +5,14 @@ module FactoryBot
5
5
 
6
6
  self.aliases = [
7
7
  [/(.+)_id/, '\1'],
8
- [/(.*)/, '\1_id'],
8
+ [/(.*)/, '\1_id']
9
9
  ]
10
10
 
11
11
  def self.aliases_for(attribute)
12
- aliases.map do |(pattern, replace)|
12
+ aliases.map { |(pattern, replace)|
13
13
  if pattern.match(attribute.to_s)
14
14
  attribute.to_s.sub(pattern, replace).to_sym
15
15
  end
16
- end.compact << attribute
16
+ }.compact << attribute
17
17
  end
18
18
  end
@@ -6,12 +6,12 @@ module FactoryBot
6
6
 
7
7
  def initialize(name, factory, overrides)
8
8
  super(name, false)
9
- @factory = factory
9
+ @factory = factory
10
10
  @overrides = overrides
11
11
  end
12
12
 
13
13
  def to_proc
14
- factory = @factory
14
+ factory = @factory
15
15
  overrides = @overrides
16
16
  traits_and_overrides = [factory, overrides].flatten
17
17
  factory_name = traits_and_overrides.shift
@@ -14,8 +14,9 @@ module FactoryBot
14
14
  value = case block.arity
15
15
  when 1, -1 then instance_exec(self, &block)
16
16
  else instance_exec(&block)
17
- end
17
+ end
18
18
  raise SequenceAbuseError if FactoryBot::Sequence === value
19
+
19
20
  value
20
21
  }
21
22
  end
@@ -3,9 +3,9 @@ module FactoryBot
3
3
  class AttributeAssigner
4
4
  def initialize(evaluator, build_class, &instance_builder)
5
5
  @build_class = build_class
6
- @instance_builder = instance_builder
7
- @evaluator = evaluator
8
- @attribute_list = evaluator.class.attribute_list
6
+ @instance_builder = instance_builder
7
+ @evaluator = evaluator
8
+ @attribute_list = evaluator.class.attribute_list
9
9
  @attribute_names_assigned = []
10
10
  end
11
11
 
@@ -22,9 +22,8 @@ module FactoryBot
22
22
  def hash
23
23
  @evaluator.instance = build_hash
24
24
 
25
- attributes_to_set_on_hash.reduce({}) do |result, attribute|
25
+ attributes_to_set_on_hash.each_with_object({}) do |attribute, result|
26
26
  result[attribute] = get(attribute)
27
- result
28
27
  end
29
28
  end
30
29
 
@@ -33,13 +32,13 @@ module FactoryBot
33
32
  def method_tracking_evaluator
34
33
  @method_tracking_evaluator ||= Decorator::AttributeHash.new(
35
34
  decorated_evaluator,
36
- attribute_names_to_assign,
35
+ attribute_names_to_assign
37
36
  )
38
37
  end
39
38
 
40
39
  def decorated_evaluator
41
40
  Decorator::InvocationTracker.new(
42
- Decorator::NewConstructor.new(@evaluator, @build_class),
41
+ Decorator::NewConstructor.new(@evaluator, @build_class)
43
42
  )
44
43
  end
45
44
 
@@ -96,11 +95,11 @@ module FactoryBot
96
95
  end
97
96
 
98
97
  def alias_names_to_ignore
99
- @attribute_list.non_ignored.flat_map do |attribute|
98
+ @attribute_list.non_ignored.flat_map { |attribute|
100
99
  override_names.map do |override|
101
100
  attribute.name if ignorable_alias?(attribute, override)
102
101
  end
103
- end.compact
102
+ }.compact
104
103
  end
105
104
 
106
105
  def ignorable_alias?(attribute, override)
@@ -4,7 +4,7 @@ module FactoryBot
4
4
  include Enumerable
5
5
 
6
6
  def initialize(name = nil, attributes = [])
7
- @name = name
7
+ @name = name
8
8
  @attributes = attributes
9
9
  end
10
10
 
@@ -3,16 +3,15 @@ module FactoryBot
3
3
  attr_reader :name
4
4
 
5
5
  def initialize(name, block)
6
- @name = name.to_sym
6
+ @name = name.to_sym
7
7
  @block = block
8
- ensure_valid_callback_name!
9
8
  end
10
9
 
11
10
  def run(instance, evaluator)
12
11
  case block.arity
13
12
  when 1, -1 then syntax_runner.instance_exec(instance, &block)
14
13
  when 2 then syntax_runner.instance_exec(instance, evaluator, &block)
15
- else syntax_runner.instance_exec(&block)
14
+ else syntax_runner.instance_exec(&block)
16
15
  end
17
16
  end
18
17
 
@@ -27,13 +26,6 @@ module FactoryBot
27
26
 
28
27
  private
29
28
 
30
- def ensure_valid_callback_name!
31
- unless FactoryBot.callback_names.include?(name)
32
- raise InvalidCallbackNameError, "#{name} is not a valid callback name. " +
33
- "Valid callback names are #{FactoryBot.callback_names.inspect}"
34
- end
35
- end
36
-
37
29
  def syntax_runner
38
30
  @syntax_runner ||= SyntaxRunner.new
39
31
  end
@@ -7,16 +7,16 @@ module FactoryBot
7
7
  :inline_sequences,
8
8
  :sequences,
9
9
  :strategies,
10
- :traits,
10
+ :traits
11
11
  )
12
12
 
13
13
  def initialize
14
- @factories = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Factory"))
15
- @sequences = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Sequence"))
16
- @traits = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Trait"))
17
- @strategies = Registry.new("Strategy")
14
+ @factories = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Factory"))
15
+ @sequences = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Sequence"))
16
+ @traits = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Trait"))
17
+ @strategies = Registry.new("Strategy")
18
18
  @callback_names = Set.new
19
- @definition = Definition.new(:configuration)
19
+ @definition = Definition.new(:configuration)
20
20
  @inline_sequences = []
21
21
 
22
22
  to_create(&:save!)
@@ -8,7 +8,7 @@ module FactoryBot
8
8
  attr_reader :name
9
9
 
10
10
  def initialize(name, ignored = false)
11
- @name = name
11
+ @name = name
12
12
  @ignored = ignored
13
13
  end
14
14