factory_bot 4.10.0 → 6.0.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/CONTRIBUTING.md +52 -13
  4. data/GETTING_STARTED.md +613 -182
  5. data/LICENSE +1 -1
  6. data/NEWS.md +358 -0
  7. data/README.md +30 -26
  8. data/lib/factory_bot.rb +71 -140
  9. data/lib/factory_bot/aliases.rb +2 -2
  10. data/lib/factory_bot/attribute.rb +4 -39
  11. data/lib/factory_bot/attribute/association.rb +2 -2
  12. data/lib/factory_bot/attribute/dynamic.rb +2 -1
  13. data/lib/factory_bot/attribute_assigner.rb +24 -10
  14. data/lib/factory_bot/attribute_list.rb +3 -2
  15. data/lib/factory_bot/callback.rb +3 -10
  16. data/lib/factory_bot/configuration.rb +15 -19
  17. data/lib/factory_bot/declaration.rb +5 -5
  18. data/lib/factory_bot/declaration/association.rb +14 -1
  19. data/lib/factory_bot/declaration/dynamic.rb +3 -1
  20. data/lib/factory_bot/declaration/implicit.rb +7 -2
  21. data/lib/factory_bot/declaration_list.rb +3 -3
  22. data/lib/factory_bot/decorator.rb +5 -5
  23. data/lib/factory_bot/decorator/attribute_hash.rb +1 -1
  24. data/lib/factory_bot/decorator/invocation_tracker.rb +1 -1
  25. data/lib/factory_bot/definition.rb +49 -20
  26. data/lib/factory_bot/definition_hierarchy.rb +1 -11
  27. data/lib/factory_bot/definition_proxy.rb +125 -43
  28. data/lib/factory_bot/enum.rb +27 -0
  29. data/lib/factory_bot/errors.rb +7 -4
  30. data/lib/factory_bot/evaluation.rb +1 -1
  31. data/lib/factory_bot/evaluator.rb +9 -11
  32. data/lib/factory_bot/evaluator_class_definer.rb +1 -1
  33. data/lib/factory_bot/factory.rb +12 -12
  34. data/lib/factory_bot/factory_runner.rb +4 -4
  35. data/lib/factory_bot/find_definitions.rb +2 -2
  36. data/lib/factory_bot/internal.rb +91 -0
  37. data/lib/factory_bot/linter.rb +41 -28
  38. data/lib/factory_bot/null_factory.rb +13 -4
  39. data/lib/factory_bot/null_object.rb +2 -6
  40. data/lib/factory_bot/registry.rb +17 -8
  41. data/lib/factory_bot/reload.rb +2 -3
  42. data/lib/factory_bot/sequence.rb +5 -6
  43. data/lib/factory_bot/strategy/stub.rb +37 -37
  44. data/lib/factory_bot/strategy_calculator.rb +1 -1
  45. data/lib/factory_bot/strategy_syntax_method_registrar.rb +13 -2
  46. data/lib/factory_bot/syntax.rb +2 -2
  47. data/lib/factory_bot/syntax/default.rb +12 -24
  48. data/lib/factory_bot/syntax/methods.rb +32 -9
  49. data/lib/factory_bot/trait.rb +7 -4
  50. data/lib/factory_bot/version.rb +1 -1
  51. metadata +50 -65
  52. data/.autotest +0 -9
  53. data/.gitignore +0 -10
  54. data/.rspec +0 -3
  55. data/.simplecov +0 -4
  56. data/.travis.yml +0 -58
  57. data/Appraisals +0 -23
  58. data/Gemfile +0 -8
  59. data/Gemfile.lock +0 -103
  60. data/NEWS +0 -298
  61. data/Rakefile +0 -36
  62. data/cucumber.yml +0 -1
  63. data/factory_bot.gemspec +0 -36
  64. data/factory_girl.gemspec +0 -40
  65. data/gemfiles/3.2.gemfile +0 -10
  66. data/gemfiles/3.2.gemfile.lock +0 -107
  67. data/gemfiles/4.0.gemfile +0 -10
  68. data/gemfiles/4.0.gemfile.lock +0 -107
  69. data/gemfiles/4.1.gemfile +0 -10
  70. data/gemfiles/4.1.gemfile.lock +0 -106
  71. data/gemfiles/4.2.gemfile +0 -10
  72. data/gemfiles/4.2.gemfile.lock +0 -106
  73. data/gemfiles/5.0.gemfile +0 -10
  74. data/gemfiles/5.0.gemfile.lock +0 -104
  75. data/gemfiles/5.1.gemfile +0 -10
  76. data/gemfiles/5.1.gemfile.lock +0 -104
  77. data/lib/factory_bot/attribute/static.rb +0 -16
  78. data/lib/factory_bot/declaration/static.rb +0 -26
  79. data/lib/factory_bot/decorator/class_key_hash.rb +0 -28
  80. data/lib/factory_girl.rb +0 -5
@@ -1,59 +1,61 @@
1
- require 'set'
2
- require 'active_support/core_ext/module/delegation'
3
- require 'active_support/deprecation'
4
- require 'active_support/notifications'
5
-
6
- require 'factory_bot/definition_hierarchy'
7
- require 'factory_bot/configuration'
8
- require 'factory_bot/errors'
9
- require 'factory_bot/factory_runner'
10
- require 'factory_bot/strategy_syntax_method_registrar'
11
- require 'factory_bot/strategy_calculator'
12
- require 'factory_bot/strategy/build'
13
- require 'factory_bot/strategy/create'
14
- require 'factory_bot/strategy/attributes_for'
15
- require 'factory_bot/strategy/stub'
16
- require 'factory_bot/strategy/null'
17
- require 'factory_bot/registry'
18
- require 'factory_bot/null_factory'
19
- require 'factory_bot/null_object'
20
- require 'factory_bot/evaluation'
21
- require 'factory_bot/factory'
22
- require 'factory_bot/attribute_assigner'
23
- require 'factory_bot/evaluator'
24
- require 'factory_bot/evaluator_class_definer'
25
- require 'factory_bot/attribute'
26
- require 'factory_bot/callback'
27
- require 'factory_bot/callbacks_observer'
28
- require 'factory_bot/declaration_list'
29
- require 'factory_bot/declaration'
30
- require 'factory_bot/sequence'
31
- require 'factory_bot/attribute_list'
32
- require 'factory_bot/trait'
33
- require 'factory_bot/aliases'
34
- require 'factory_bot/definition'
35
- require 'factory_bot/definition_proxy'
36
- require 'factory_bot/syntax'
37
- require 'factory_bot/syntax_runner'
38
- require 'factory_bot/find_definitions'
39
- require 'factory_bot/reload'
40
- require 'factory_bot/decorator'
41
- require 'factory_bot/decorator/attribute_hash'
42
- require 'factory_bot/decorator/class_key_hash'
43
- require 'factory_bot/decorator/disallows_duplicates_registry'
44
- require 'factory_bot/decorator/invocation_tracker'
45
- require 'factory_bot/decorator/new_constructor'
46
- require 'factory_bot/linter'
47
- require 'factory_bot/version'
1
+ require "set"
2
+ require "active_support/core_ext/module/delegation"
3
+ require "active_support/core_ext/module/attribute_accessors"
4
+ require "active_support/deprecation"
5
+ require "active_support/notifications"
6
+
7
+ require "factory_bot/internal"
8
+ require "factory_bot/definition_hierarchy"
9
+ require "factory_bot/configuration"
10
+ require "factory_bot/errors"
11
+ require "factory_bot/factory_runner"
12
+ require "factory_bot/strategy_syntax_method_registrar"
13
+ require "factory_bot/strategy_calculator"
14
+ require "factory_bot/strategy/build"
15
+ require "factory_bot/strategy/create"
16
+ require "factory_bot/strategy/attributes_for"
17
+ require "factory_bot/strategy/stub"
18
+ require "factory_bot/strategy/null"
19
+ require "factory_bot/registry"
20
+ require "factory_bot/null_factory"
21
+ require "factory_bot/null_object"
22
+ require "factory_bot/evaluation"
23
+ require "factory_bot/factory"
24
+ require "factory_bot/attribute_assigner"
25
+ require "factory_bot/evaluator"
26
+ require "factory_bot/evaluator_class_definer"
27
+ require "factory_bot/attribute"
28
+ require "factory_bot/callback"
29
+ require "factory_bot/callbacks_observer"
30
+ require "factory_bot/declaration_list"
31
+ require "factory_bot/declaration"
32
+ require "factory_bot/sequence"
33
+ require "factory_bot/attribute_list"
34
+ require "factory_bot/trait"
35
+ require "factory_bot/enum"
36
+ require "factory_bot/aliases"
37
+ require "factory_bot/definition"
38
+ require "factory_bot/definition_proxy"
39
+ require "factory_bot/syntax"
40
+ require "factory_bot/syntax_runner"
41
+ require "factory_bot/find_definitions"
42
+ require "factory_bot/reload"
43
+ require "factory_bot/decorator"
44
+ require "factory_bot/decorator/attribute_hash"
45
+ require "factory_bot/decorator/disallows_duplicates_registry"
46
+ require "factory_bot/decorator/invocation_tracker"
47
+ require "factory_bot/decorator/new_constructor"
48
+ require "factory_bot/linter"
49
+ require "factory_bot/version"
48
50
 
49
51
  module FactoryBot
50
- def self.configuration
51
- @configuration ||= Configuration.new
52
- end
52
+ Deprecation = ActiveSupport::Deprecation.new("7.0", "factory_bot")
53
53
 
54
- def self.reset_configuration
55
- @configuration = nil
56
- end
54
+ mattr_accessor :use_parent_strategy, instance_accessor: false
55
+ self.use_parent_strategy = true
56
+
57
+ mattr_accessor :automatically_define_enum_traits, instance_accessor: false
58
+ self.automatically_define_enum_traits = true
57
59
 
58
60
  # Look for errors in factories and (optionally) their traits.
59
61
  # Parameters:
@@ -61,100 +63,29 @@ module FactoryBot
61
63
  # options:
62
64
  # traits: true - to lint traits as well as factories
63
65
  # strategy: :create - to specify the strategy for linting
66
+ # verbose: true - to include full backtraces for each linting error
64
67
  def self.lint(*args)
65
68
  options = args.extract_options!
66
69
  factories_to_lint = args[0] || FactoryBot.factories
67
- linting_strategy = options[:traits] ? :factory_and_traits : :factory
68
- factory_strategy = options[:strategy] || :create
69
- Linter.new(factories_to_lint, linting_strategy, factory_strategy).lint!
70
- end
71
-
72
- class << self
73
- delegate :factories,
74
- :sequences,
75
- :traits,
76
- :callbacks,
77
- :strategies,
78
- :callback_names,
79
- :to_create,
80
- :skip_create,
81
- :initialize_with,
82
- :constructor,
83
- :duplicate_attribute_assignment_from_initialize_with,
84
- :duplicate_attribute_assignment_from_initialize_with=,
85
- :allow_class_lookup,
86
- :allow_class_lookup=,
87
- :use_parent_strategy,
88
- :use_parent_strategy=,
89
- to: :configuration
90
- end
91
-
92
- def self.register_factory(factory)
93
- factory.names.each do |name|
94
- factories.register(name, factory)
95
- end
96
- factory
97
- end
98
-
99
- def self.factory_by_name(name)
100
- factories.find(name)
101
- end
102
-
103
- def self.register_sequence(sequence)
104
- sequence.names.each do |name|
105
- sequences.register(name, sequence)
106
- end
107
- sequence
108
- end
109
-
110
- def self.sequence_by_name(name)
111
- sequences.find(name)
112
- end
113
-
114
- def self.rewind_sequences
115
- sequences.each(&:rewind)
116
- end
117
-
118
- def self.register_trait(trait)
119
- trait.names.each do |name|
120
- traits.register(name, trait)
121
- end
122
- trait
123
- end
124
-
125
- def self.trait_by_name(name)
126
- traits.find(name)
70
+ Linter.new(factories_to_lint, **options).lint!
127
71
  end
128
72
 
129
- def self.register_strategy(strategy_name, strategy_class)
130
- strategies.register(strategy_name, strategy_class)
131
- 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
132
80
  end
133
81
 
134
- def self.strategy_by_name(name)
135
- strategies.find(name)
136
- end
137
-
138
- def self.register_default_strategies
139
- register_strategy(:build, FactoryBot::Strategy::Build)
140
- register_strategy(:create, FactoryBot::Strategy::Create)
141
- register_strategy(:attributes_for, FactoryBot::Strategy::AttributesFor)
142
- register_strategy(:build_stubbed, FactoryBot::Strategy::Stub)
143
- register_strategy(:null, FactoryBot::Strategy::Null)
144
- end
145
-
146
- def self.register_default_callbacks
147
- register_callback(:after_build)
148
- register_callback(:after_create)
149
- register_callback(:after_stub)
150
- register_callback(:before_create)
151
- end
152
-
153
- def self.register_callback(name)
154
- name = name.to_sym
155
- callback_names << name
82
+ class << self
83
+ delegate :factories,
84
+ :register_strategy,
85
+ :rewind_sequences,
86
+ :strategy_by_name,
87
+ to: Internal
156
88
  end
157
89
  end
158
90
 
159
- FactoryBot.register_default_strategies
160
- FactoryBot.register_default_callbacks
91
+ FactoryBot::Internal.register_default_strategies
@@ -9,10 +9,10 @@ module FactoryBot
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
@@ -1,7 +1,6 @@
1
- require 'factory_bot/attribute/static'
2
- require 'factory_bot/attribute/dynamic'
3
- require 'factory_bot/attribute/association'
4
- require 'factory_bot/attribute/sequence'
1
+ require "factory_bot/attribute/dynamic"
2
+ require "factory_bot/attribute/association"
3
+ require "factory_bot/attribute/sequence"
5
4
 
6
5
  module FactoryBot
7
6
  # @api private
@@ -11,11 +10,10 @@ module FactoryBot
11
10
  def initialize(name, ignored)
12
11
  @name = name.to_sym
13
12
  @ignored = ignored
14
- ensure_non_attribute_writer!
15
13
  end
16
14
 
17
15
  def to_proc
18
- -> { }
16
+ -> {}
19
17
  end
20
18
 
21
19
  def association?
@@ -25,38 +23,5 @@ module FactoryBot
25
23
  def alias_for?(attr)
26
24
  FactoryBot.aliases_for(attr).include?(name)
27
25
  end
28
-
29
- private
30
-
31
- def ensure_non_attribute_writer!
32
- NonAttributeWriterValidator.new(@name).validate!
33
- end
34
-
35
- class NonAttributeWriterValidator
36
- def initialize(method_name)
37
- @method_name = method_name.to_s
38
- @method_name_setter_match = @method_name.match(/(.*)=$/)
39
- end
40
-
41
- def validate!
42
- if method_is_writer?
43
- raise AttributeDefinitionError, error_message
44
- end
45
- end
46
-
47
- private
48
-
49
- def method_is_writer?
50
- !!@method_name_setter_match
51
- end
52
-
53
- def attribute_name
54
- @method_name_setter_match[1]
55
- end
56
-
57
- def error_message
58
- "factory_bot uses '#{attribute_name} value' syntax rather than '#{attribute_name} = value'"
59
- end
60
- end
61
26
  end
62
27
  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,16 +22,18 @@ module FactoryBot
22
22
  def hash
23
23
  @evaluator.instance = build_hash
24
24
 
25
- attributes_to_set_on_hash.inject({}) 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
 
31
30
  private
32
31
 
33
32
  def method_tracking_evaluator
34
- @method_tracking_evaluator ||= Decorator::AttributeHash.new(decorated_evaluator, attribute_names_to_assign)
33
+ @method_tracking_evaluator ||= Decorator::AttributeHash.new(
34
+ decorated_evaluator,
35
+ attribute_names_to_assign
36
+ )
35
37
  end
36
38
 
37
39
  def decorated_evaluator
@@ -65,7 +67,11 @@ module FactoryBot
65
67
  end
66
68
 
67
69
  def attribute_names_to_assign
68
- @attribute_names_to_assign ||= non_ignored_attribute_names + override_names - ignored_attribute_names - alias_names_to_ignore
70
+ @attribute_names_to_assign ||=
71
+ non_ignored_attribute_names +
72
+ override_names -
73
+ ignored_attribute_names -
74
+ alias_names_to_ignore
69
75
  end
70
76
 
71
77
  def non_ignored_attribute_names
@@ -89,9 +95,17 @@ module FactoryBot
89
95
  end
90
96
 
91
97
  def alias_names_to_ignore
92
- @attribute_list.non_ignored.flat_map do |attribute|
93
- override_names.map { |override| attribute.name if attribute.alias_for?(override) && attribute.name != override && !ignored_attribute_names.include?(override) }
94
- end.compact
98
+ @attribute_list.non_ignored.flat_map { |attribute|
99
+ override_names.map do |override|
100
+ attribute.name if ignorable_alias?(attribute, override)
101
+ end
102
+ }.compact
103
+ end
104
+
105
+ def ignorable_alias?(attribute, override)
106
+ attribute.alias_for?(override) &&
107
+ attribute.name != override &&
108
+ !ignored_attribute_names.include?(override)
95
109
  end
96
110
  end
97
111
  end
@@ -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
 
@@ -54,7 +54,8 @@ module FactoryBot
54
54
 
55
55
  def ensure_attribute_not_self_referencing!(attribute)
56
56
  if attribute.respond_to?(:factory) && attribute.factory == @name
57
- raise AssociationDefinitionError, "Self-referencing association '#{attribute.name}' in '#{attribute.factory}'"
57
+ message = "Self-referencing association '#{attribute.name}' in '#{attribute.factory}'"
58
+ raise AssociationDefinitionError, message
58
59
  end
59
60
  end
60
61
 
@@ -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
 
@@ -22,17 +21,11 @@ module FactoryBot
22
21
  end
23
22
 
24
23
  protected
24
+
25
25
  attr_reader :block
26
26
 
27
27
  private
28
28
 
29
- def ensure_valid_callback_name!
30
- unless FactoryBot.callback_names.include?(name)
31
- raise InvalidCallbackNameError, "#{name} is not a valid callback name. " +
32
- "Valid callback names are #{FactoryBot.callback_names.inspect}"
33
- end
34
- end
35
-
36
29
  def syntax_runner
37
30
  @syntax_runner ||= SyntaxRunner.new
38
31
  end