factory_bot 5.0.1 → 5.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: 3ae3cef3f2df889e63bd9eb74cd8cff214994748287c73c2ada84e7983d4fe3f
4
- data.tar.gz: 3b5f5e43645af018b65fe78e9ede19544976daac161272310c865b2d6d7df365
3
+ metadata.gz: 802da48aa95dce3321f20d0173c5dcf041c5393fb0c270fb8ae2029ce12b6ed9
4
+ data.tar.gz: 0aa227896ef66ccf100f88b1aa4e9aa26c5bc657fee76739a73e15072bc80572
5
5
  SHA512:
6
- metadata.gz: b7c1c0dfeadb5bab62335f4fdf4ac3db1bc968fc6c24a26402f63f670fb3b1f90249dd7d31122509cc867f8354c3c326ded4790a7dcdb5b178b0e7f5a8e359d5
7
- data.tar.gz: 5059c10e6cb958fd5875f8196e3187dde9d9bca62ab05bd85a4c5d5208c57bdd7d404cb096e2654fcbf599fb3d22064cbf46f97139ac4f04eac2be8cb62a3230
6
+ metadata.gz: 63554d6765398f0c524cb8cd45b1a0da83da1d8aba9f359d0e4c44e42cdbd0d93edb8e38c668e0310ad709905c5cd6a3c35e640ad8c45a06a8b7d4090b4e6161
7
+ data.tar.gz: fe3be69bdd601384da0a7f2ec7e1fcf00ae7d4984c65423e31e94c1b7f3d7bb04345371daaea19d794365972b59f69f9f3403b0ef8b8ef7d7349d89404284ac2
@@ -30,7 +30,7 @@ Configure your test suite
30
30
 
31
31
  ### RSpec
32
32
 
33
- If you're using Rails:
33
+ If you're using Rails, add the following configuration to `spec/support/factory_bot.rb` and be sure to require that file in `rails_helper.rb`:
34
34
 
35
35
  ```ruby
36
36
  RSpec.configure do |config|
@@ -387,7 +387,7 @@ post.new_record? # => true
387
387
  post.author.new_record? # => false
388
388
  ```
389
389
 
390
- To not save the associated object, specify strategy: :build in the factory:
390
+ To not save the associated object, specify `strategy: :build` in the factory:
391
391
 
392
392
  ```ruby
393
393
  FactoryBot.use_parent_strategy = false
@@ -510,20 +510,20 @@ create(:profile_with_languages, languages_count: 15).languages.length # 15
510
510
 
511
511
  Polymorphic associations can be handled with traits:
512
512
 
513
- ```
513
+ ```ruby
514
514
  FactoryBot.define do
515
515
  factory :video
516
516
  factory :photo
517
517
 
518
518
  factory :comment do
519
- for_photo
519
+ for_photo # default to the :for_photo trait if none is specified
520
520
 
521
521
  trait :for_video do
522
- association(:commentable, factory: :video)
522
+ association :commentable, factory: :video
523
523
  end
524
524
 
525
525
  trait :for_photo do
526
- association(:commentable, factory: :photo)
526
+ association :commentable, factory: :photo
527
527
  end
528
528
  end
529
529
  end
@@ -531,7 +531,7 @@ end
531
531
 
532
532
  This allows us to do:
533
533
 
534
- ```
534
+ ```ruby
535
535
  create(:comment)
536
536
  create(:comment, :for_video)
537
537
  create(:comment, :for_photo)
@@ -961,7 +961,7 @@ If a gem were to give you a User factory:
961
961
  ```ruby
962
962
  FactoryBot.define do
963
963
  factory :user do
964
- full_name "John Doe"
964
+ full_name { "John Doe" }
965
965
  sequence(:username) { |n| "user#{n}" }
966
966
  password { "password" }
967
967
  end
@@ -1018,6 +1018,14 @@ To set the attributes for each of the factories, you can pass in a hash as you n
1018
1018
  twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago)
1019
1019
  ```
1020
1020
 
1021
+ In order to set different attributes for each factory, these methods may be passed a block, with the factory and the index as parameters:
1022
+
1023
+ ```ruby
1024
+ twenty_somethings = build_list(:user, 10) do |user, i|
1025
+ user.date_of_birth = (20 + i).years.ago
1026
+ end
1027
+ ```
1028
+
1021
1029
  `build_stubbed_list` will give you fully stubbed out instances:
1022
1030
 
1023
1031
  ```ruby
@@ -1067,9 +1075,10 @@ namespace :factory_bot do
1067
1075
  desc "Verify that all FactoryBot factories are valid"
1068
1076
  task lint: :environment do
1069
1077
  if Rails.env.test?
1070
- DatabaseCleaner.clean_with(:deletion)
1071
- DatabaseCleaner.cleaning do
1078
+ conn = ActiveRecord::Base.connection
1079
+ conn.transaction do
1072
1080
  FactoryBot.lint
1081
+ raise ActiveRecord::Rollback
1073
1082
  end
1074
1083
  else
1075
1084
  system("bundle exec rake factory_bot:lint RAILS_ENV='test'")
@@ -1081,8 +1090,7 @@ end
1081
1090
 
1082
1091
  After calling `FactoryBot.lint`, you'll likely want to clear out the
1083
1092
  database, as records will most likely be created. The provided example above
1084
- uses the database_cleaner gem to clear out the database; be sure to add the
1085
- gem to your Gemfile under the appropriate groups.
1093
+ uses an sql transaction and rollback to leave the database clean.
1086
1094
 
1087
1095
  You can lint factories selectively by passing only factories you want linted:
1088
1096
 
@@ -1413,12 +1421,12 @@ with associations, as below:
1413
1421
 
1414
1422
  ```ruby
1415
1423
  FactoryBot.define do
1416
- factory :united_states, class: Location do
1424
+ factory :united_states, class: "Location" do
1417
1425
  name { 'United States' }
1418
1426
  association :location_group, factory: :north_america
1419
1427
  end
1420
1428
 
1421
- factory :north_america, class: LocationGroup do
1429
+ factory :north_america, class: "LocationGroup" do
1422
1430
  name { 'North America' }
1423
1431
  end
1424
1432
  end
data/NEWS.md CHANGED
@@ -1,10 +1,32 @@
1
1
  # News
2
2
 
3
+ ## 5.2.0 (April 24, 2020)
4
+ * Added: Pass index to block for `*_list` methods
5
+ * Deprecated: top-level methods meant only for internal use: `callbacks`, `configuration`, `constructor`, `initialize_with`, `register_sequence`, `resent_configuration`, `skip_create`, `to_create`
6
+
7
+ ## 5.1.2 (March 25, 2020)
8
+ * Fixed: Ruby 2.7 keyword deprecation warning in FactoryBot.lint
9
+
10
+ ## 5.1.1 (October 2, 2019)
11
+ * Improved: performance of traits
12
+ * Fixed: registering strategies on JRuby
13
+
14
+ ## 5.1.0 (September 21, 2019)
15
+ * Added: "Did you mean?" style error message to help with typos in association declarations
16
+ * Changed: `NoMethodError` for static attributes now offers a "Did you mean?" style message
17
+ * Fixed: avoid undefining inherited evaluator methods
18
+ * Fixed: avoid stubbing id for records without a primary key
19
+ * Fixed: raise a helpful error for self-referencing traits to avoid a `SystemStackError`
20
+ * Deprecated: top-level methods 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`
21
+
22
+ ## 5.0.2 (February 22, 2019)
23
+ * Bugfix: raise "Trait not registered" error when passing invalid trait arguments
24
+
3
25
  ## 5.0.1 (February 15, 2019)
4
26
  * Bugfix: Do not raise error when two sequences have the same name
5
27
  in two traits that have the same name
6
28
 
7
- ## 5.0.0
29
+ ## 5.0.0 (February 1, 2019)
8
30
  * Added: Verbose option to include full backtraces in the linting output
9
31
  * Changed: use_parent_strategy now defaults to true, so by default the
10
32
  build strategy will build, rather than create associations
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,30 +59,37 @@ 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
  ------------
68
72
 
69
73
  Please see [CONTRIBUTING.md](https://github.com/thoughtbot/factory_bot/blob/master/CONTRIBUTING.md).
70
74
 
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
75
+ factory_bot was originally written by Joe Ferris and is maintained by thoughtbot.
76
+ Many improvements and bugfixes were contributed by the [open source
73
77
  community](https://github.com/thoughtbot/factory_bot/graphs/contributors).
74
78
 
75
79
  License
76
80
  -------
77
81
 
78
- factory_bot is Copyright © 2008-2019 Joe Ferris and thoughtbot. It is free
82
+ factory_bot is Copyright © 2008-2020 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.
@@ -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"
@@ -45,18 +46,9 @@ require "factory_bot/decorator/invocation_tracker"
45
46
  require "factory_bot/decorator/new_constructor"
46
47
  require "factory_bot/linter"
47
48
  require "factory_bot/version"
48
- require "factory_bot/internal"
49
49
 
50
50
  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
51
+ Deprecation = ActiveSupport::Deprecation.new("6.0", "factory_bot")
60
52
 
61
53
  mattr_accessor :use_parent_strategy, instance_accessor: false
62
54
  self.use_parent_strategy = true
@@ -71,93 +63,63 @@ module FactoryBot
71
63
  def self.lint(*args)
72
64
  options = args.extract_options!
73
65
  factories_to_lint = args[0] || FactoryBot.factories
74
- Linter.new(factories_to_lint, options).lint!
66
+ Linter.new(factories_to_lint, **options).lint!
75
67
  end
76
68
 
77
69
  class << self
78
- delegate :factories,
79
- :sequences,
80
- :traits,
70
+ delegate :callback_names,
81
71
  :callbacks,
72
+ :configuration,
73
+ :constructor,
74
+ :factories,
75
+ :factory_by_name,
76
+ :initialize_with,
77
+ :register_callback,
78
+ :register_default_callbacks,
79
+ :register_default_strategies,
80
+ :register_factory,
81
+ :register_sequence,
82
+ :register_strategy,
83
+ :register_trait,
84
+ :reset_configuration,
85
+ :rewind_sequences,
86
+ :sequence_by_name,
87
+ :sequences,
88
+ :skip_create,
82
89
  :strategies,
83
- :callback_names,
90
+ :strategy_by_name,
84
91
  :to_create,
85
- :skip_create,
86
- :initialize_with,
87
- :constructor,
88
- to: :configuration
92
+ :trait_by_name,
93
+ :traits,
94
+ to: Internal
89
95
 
90
96
  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)
130
- end
131
-
132
- def self.register_strategy(strategy_name, strategy_class)
133
- strategies.register(strategy_name, strategy_class)
134
- StrategySyntaxMethodRegistrar.new(strategy_name).define_strategy_methods
135
- end
136
-
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
97
 
156
- def self.register_callback(name)
157
- name = name.to_sym
158
- callback_names << name
98
+ deprecate :allow_class_lookup,
99
+ :allow_class_lookup=,
100
+ :callback_names,
101
+ :callbacks,
102
+ :configuration,
103
+ :constructor,
104
+ :factory_by_name,
105
+ :initialize_with,
106
+ :register_callback,
107
+ :register_default_callbacks,
108
+ :register_default_strategies,
109
+ :register_factory,
110
+ :register_sequence,
111
+ :register_trait,
112
+ :reset_configuration,
113
+ :sequence_by_name,
114
+ :sequences,
115
+ :skip_create,
116
+ :strategies,
117
+ :to_create,
118
+ :trait_by_name,
119
+ :traits,
120
+ deprecator: Deprecation
159
121
  end
160
122
  end
161
123
 
162
- FactoryBot.register_default_strategies
163
- 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
 
@@ -24,7 +24,7 @@ module FactoryBot
24
24
  end
25
25
 
26
26
  delegate :to_create, :skip_create, :constructor, :before, :after,
27
- :callback, :callbacks, to: :@definition
27
+ :callback, :callbacks, to: :@definition
28
28
 
29
29
  def initialize_with(&block)
30
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)
@@ -1,16 +1,6 @@
1
1
  module FactoryBot
2
2
  class DefinitionHierarchy
3
- def callbacks
4
- FactoryBot.callbacks
5
- end
6
-
7
- def constructor
8
- FactoryBot.constructor
9
- end
10
-
11
- def to_create
12
- FactoryBot.to_create
13
- end
3
+ delegate :callbacks, :constructor, :to_create, to: Internal
14
4
 
15
5
  def self.build_from_definition(definition)
16
6
  build_to_create(&definition.to_create)
@@ -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
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
 
@@ -187,5 +190,9 @@ module FactoryBot
187
190
  add_attribute(name, &block)
188
191
  end
189
192
  end
193
+
194
+ def __valid_association_options?(options)
195
+ options.respond_to?(:has_key?) && options.has_key?(:factory)
196
+ end
190
197
  end
191
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
 
@@ -2,7 +2,20 @@ module FactoryBot
2
2
  # @api private
3
3
  module Internal
4
4
  class << self
5
- delegate :inline_sequences, to: :configuration
5
+ delegate :after,
6
+ :before,
7
+ :callback_names,
8
+ :callbacks,
9
+ :constructor,
10
+ :factories,
11
+ :initialize_with,
12
+ :inline_sequences,
13
+ :sequences,
14
+ :skip_create,
15
+ :strategies,
16
+ :to_create,
17
+ :traits,
18
+ to: :configuration
6
19
 
7
20
  def configuration
8
21
  @configuration ||= Configuration.new
@@ -19,6 +32,73 @@ module FactoryBot
19
32
  def rewind_inline_sequences
20
33
  inline_sequences.each(&:rewind)
21
34
  end
35
+
36
+ def register_trait(trait)
37
+ trait.names.each do |name|
38
+ traits.register(name, trait)
39
+ end
40
+ trait
41
+ end
42
+
43
+ def trait_by_name(name)
44
+ traits.find(name)
45
+ end
46
+
47
+ def register_sequence(sequence)
48
+ sequence.names.each do |name|
49
+ sequences.register(name, sequence)
50
+ end
51
+ sequence
52
+ end
53
+
54
+ def sequence_by_name(name)
55
+ sequences.find(name)
56
+ end
57
+
58
+ def rewind_sequences
59
+ sequences.each(&:rewind)
60
+ rewind_inline_sequences
61
+ end
62
+
63
+ def register_factory(factory)
64
+ factory.names.each do |name|
65
+ factories.register(name, factory)
66
+ end
67
+ factory
68
+ end
69
+
70
+ def factory_by_name(name)
71
+ factories.find(name)
72
+ end
73
+
74
+ def register_strategy(strategy_name, strategy_class)
75
+ strategies.register(strategy_name, strategy_class)
76
+ StrategySyntaxMethodRegistrar.new(strategy_name).define_strategy_methods
77
+ end
78
+
79
+ def strategy_by_name(name)
80
+ strategies.find(name)
81
+ end
82
+
83
+ def register_default_strategies
84
+ register_strategy(:build, FactoryBot::Strategy::Build)
85
+ register_strategy(:create, FactoryBot::Strategy::Create)
86
+ register_strategy(:attributes_for, FactoryBot::Strategy::AttributesFor)
87
+ register_strategy(:build_stubbed, FactoryBot::Strategy::Stub)
88
+ register_strategy(:null, FactoryBot::Strategy::Null)
89
+ end
90
+
91
+ def register_default_callbacks
92
+ register_callback(:after_create)
93
+ register_callback(:after_build)
94
+ register_callback(:after_stub)
95
+ register_callback(:before_create)
96
+ end
97
+
98
+ def register_callback(name)
99
+ name = name.to_sym
100
+ callback_names << name
101
+ end
22
102
  end
23
103
  end
24
104
  end
@@ -72,8 +72,8 @@ module FactoryBot
72
72
  result = []
73
73
  begin
74
74
  FactoryBot.public_send(factory_strategy, factory.name)
75
- rescue StandardError => error
76
- result |= [FactoryError.new(error, factory)]
75
+ rescue StandardError => e
76
+ result |= [FactoryError.new(e, factory)]
77
77
  end
78
78
  result
79
79
  end
@@ -83,9 +83,9 @@ module FactoryBot
83
83
  factory.definition.defined_traits.map(&:name).each do |trait_name|
84
84
  begin
85
85
  FactoryBot.public_send(factory_strategy, factory.name, trait_name)
86
- rescue StandardError => error
86
+ rescue StandardError => e
87
87
  result |=
88
- [FactoryTraitError.new(error, factory, trait_name)]
88
+ [FactoryTraitError.new(e, factory, trait_name)]
89
89
  end
90
90
  end
91
91
  result
@@ -8,7 +8,7 @@ module FactoryBot
8
8
  end
9
9
 
10
10
  delegate :defined_traits, :callbacks, :attributes, :constructor,
11
- :to_create, to: :definition
11
+ :to_create, to: :definition
12
12
 
13
13
  def compile; end
14
14
 
@@ -21,8 +21,8 @@ module FactoryBot
21
21
 
22
22
  def find(name)
23
23
  @items.fetch(name)
24
- rescue KeyError => key_error
25
- raise key_error_with_custom_message(key_error)
24
+ rescue KeyError => e
25
+ raise key_error_with_custom_message(e)
26
26
  end
27
27
 
28
28
  alias :[] :find
@@ -1,8 +1,8 @@
1
1
  module FactoryBot
2
2
  def self.reload
3
3
  Internal.reset_configuration
4
- register_default_strategies
5
- register_default_callbacks
4
+ Internal.register_default_strategies
5
+ Internal.register_default_callbacks
6
6
  find_definitions
7
7
  end
8
8
  end
@@ -44,15 +44,17 @@ module FactoryBot
44
44
  end
45
45
 
46
46
  def stub_database_interaction_on_result(result_instance)
47
- result_instance.id ||= next_id
47
+ if has_settable_id?(result_instance)
48
+ result_instance.id ||= next_id
49
+ end
48
50
 
49
51
  result_instance.instance_eval do
50
52
  def persisted?
51
- !new_record?
53
+ true
52
54
  end
53
55
 
54
56
  def new_record?
55
- id.nil?
57
+ false
56
58
  end
57
59
 
58
60
  def destroyed?
@@ -68,6 +70,11 @@ module FactoryBot
68
70
  end
69
71
  end
70
72
 
73
+ def has_settable_id?(result_instance)
74
+ !result_instance.class.respond_to?(:primary_key) ||
75
+ result_instance.class.primary_key
76
+ end
77
+
71
78
  def clear_changes_information(result_instance)
72
79
  if result_instance.respond_to?(:clear_changes_information)
73
80
  result_instance.clear_changes_information
@@ -20,7 +20,7 @@ module FactoryBot
20
20
  end
21
21
 
22
22
  def strategy_name_to_object
23
- FactoryBot.strategy_by_name(@name_or_object)
23
+ FactoryBot::Internal.strategy_by_name(@name_or_object)
24
24
  end
25
25
  end
26
26
  end
@@ -11,6 +11,14 @@ module FactoryBot
11
11
  define_pair_strategy_method
12
12
  end
13
13
 
14
+ def self.with_index(block, index)
15
+ if block&.arity == 2
16
+ ->(instance) { block.call(instance, index) }
17
+ else
18
+ block
19
+ end
20
+ end
21
+
14
22
  private
15
23
 
16
24
  def define_singular_strategy_method
@@ -29,7 +37,10 @@ module FactoryBot
29
37
  raise ArgumentError, "count missing for #{strategy_name}_list"
30
38
  end
31
39
 
32
- Array.new(amount) { send(strategy_name, name, *traits_and_overrides, &block) }
40
+ Array.new(amount) do |i|
41
+ block_with_index = StrategySyntaxMethodRegistrar.with_index(block, i)
42
+ send(strategy_name, name, *traits_and_overrides, &block_with_index)
43
+ end
33
44
  end
34
45
  end
35
46
 
@@ -17,7 +17,7 @@ module FactoryBot
17
17
  proxy = FactoryBot::DefinitionProxy.new(factory.definition)
18
18
  proxy.instance_eval(&block) if block_given?
19
19
 
20
- FactoryBot.register_factory(factory)
20
+ Internal.register_factory(factory)
21
21
 
22
22
  proxy.child_factories.each do |(child_name, child_options, child_block)|
23
23
  parent_factory = child_options.delete(:parent) || name
@@ -26,41 +26,29 @@ module FactoryBot
26
26
  end
27
27
 
28
28
  def sequence(name, *args, &block)
29
- FactoryBot.register_sequence(Sequence.new(name, *args, &block))
29
+ Internal.register_sequence(Sequence.new(name, *args, &block))
30
30
  end
31
31
 
32
32
  def trait(name, &block)
33
- FactoryBot.register_trait(Trait.new(name, &block))
34
- end
35
-
36
- def to_create(&block)
37
- FactoryBot.to_create(&block)
38
- end
39
-
40
- def skip_create
41
- FactoryBot.skip_create
42
- end
43
-
44
- def initialize_with(&block)
45
- FactoryBot.initialize_with(&block)
33
+ Internal.register_trait(Trait.new(name, &block))
46
34
  end
47
35
 
48
36
  def self.run(block)
49
37
  new.instance_eval(&block)
50
38
  end
51
39
 
52
- delegate :before, :after, :callback, to: :configuration
53
-
54
- private
55
-
56
- def configuration
57
- FactoryBot::Internal.configuration
58
- end
40
+ delegate :after,
41
+ :before,
42
+ :callback,
43
+ :initialize_with,
44
+ :skip_create,
45
+ :to_create,
46
+ to: FactoryBot::Internal
59
47
  end
60
48
 
61
49
  class ModifyDSL
62
50
  def factory(name, _options = {}, &block)
63
- factory = FactoryBot.factory_by_name(name)
51
+ factory = Internal.factory_by_name(name)
64
52
  proxy = FactoryBot::DefinitionProxy.new(factory.definition.overridable)
65
53
  proxy.instance_eval(&block)
66
54
  end
@@ -30,7 +30,7 @@ module FactoryBot
30
30
  ## # factory with traits and attribute override
31
31
  ## build_stubbed_list(:user, 15, :admin, :male, name: "John Doe")
32
32
  module Methods
33
- # @!parse FactoryBot.register_default_strategies
33
+ # @!parse FactoryBot::Internal.register_default_strategies
34
34
  # @!method build(name, *traits_and_overrides, &block)
35
35
  # (see #strategy_method)
36
36
  # Builds a registered factory by name.
@@ -111,7 +111,7 @@ module FactoryBot
111
111
  # Returns:
112
112
  # The next value in the sequence. (Object)
113
113
  def generate(name)
114
- FactoryBot.sequence_by_name(name).next
114
+ Internal.sequence_by_name(name).next
115
115
  end
116
116
 
117
117
  # Generates and returns the list of values in a sequence.
@@ -126,7 +126,7 @@ module FactoryBot
126
126
  # The next value in the sequence. (Object)
127
127
  def generate_list(name, count)
128
128
  (1..count).map do
129
- FactoryBot.sequence_by_name(name).next
129
+ Internal.sequence_by_name(name).next
130
130
  end
131
131
  end
132
132
  end
@@ -4,12 +4,14 @@ module FactoryBot
4
4
  attr_reader :name, :definition
5
5
 
6
6
  def initialize(name, &block)
7
- @name = name.to_sym
7
+ @name = name.to_s
8
8
  @block = block
9
9
  @definition = Definition.new(@name)
10
-
11
10
  proxy = FactoryBot::DefinitionProxy.new(@definition)
12
- proxy.instance_eval(&@block) if block_given?
11
+
12
+ if block_given?
13
+ proxy.instance_eval(&@block)
14
+ end
13
15
  end
14
16
 
15
17
  delegate :add_callback, :declare_attribute, :to_create, :define_trait, :constructor,
@@ -1,3 +1,3 @@
1
1
  module FactoryBot
2
- VERSION = "5.0.1".freeze
2
+ VERSION = "5.2.0".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: factory_bot
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.1
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Clayton
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-02-15 00:00:00.000000000 Z
12
+ date: 2020-04-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -127,18 +127,18 @@ dependencies:
127
127
  name: rubocop
128
128
  requirement: !ruby/object:Gem::Requirement
129
129
  requirements:
130
- - - '='
130
+ - - ">="
131
131
  - !ruby/object:Gem::Version
132
- version: '0.54'
132
+ version: '0'
133
133
  type: :development
134
134
  prerelease: false
135
135
  version_requirements: !ruby/object:Gem::Requirement
136
136
  requirements:
137
- - - '='
137
+ - - ">="
138
138
  - !ruby/object:Gem::Version
139
- version: '0.54'
139
+ version: '0'
140
140
  - !ruby/object:Gem::Dependency
141
- name: simplecov
141
+ name: rubocop-performance
142
142
  requirement: !ruby/object:Gem::Requirement
143
143
  requirements:
144
144
  - - ">="
@@ -152,7 +152,7 @@ dependencies:
152
152
  - !ruby/object:Gem::Version
153
153
  version: '0'
154
154
  - !ruby/object:Gem::Dependency
155
- name: sqlite3
155
+ name: rubocop-rails
156
156
  requirement: !ruby/object:Gem::Requirement
157
157
  requirements:
158
158
  - - ">="
@@ -166,7 +166,7 @@ dependencies:
166
166
  - !ruby/object:Gem::Version
167
167
  version: '0'
168
168
  - !ruby/object:Gem::Dependency
169
- name: timecop
169
+ name: simplecov
170
170
  requirement: !ruby/object:Gem::Requirement
171
171
  requirements:
172
172
  - - ">="
@@ -263,7 +263,8 @@ files:
263
263
  homepage: https://github.com/thoughtbot/factory_bot
264
264
  licenses:
265
265
  - MIT
266
- metadata: {}
266
+ metadata:
267
+ changelog_uri: https://github.com/thoughtbot/factory_bot/blob/master/NEWS.md
267
268
  post_install_message:
268
269
  rdoc_options: []
269
270
  require_paths:
@@ -279,8 +280,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
279
280
  - !ruby/object:Gem::Version
280
281
  version: '0'
281
282
  requirements: []
282
- rubyforge_project:
283
- rubygems_version: 2.7.7
283
+ rubygems_version: 3.0.3
284
284
  signing_key:
285
285
  specification_version: 4
286
286
  summary: factory_bot provides a framework and DSL for defining and using model instance