tiny-lite-mod 0.0.1
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.
- checksums.yaml +7 -0
- data/factory_bot-6.6.0/CONTRIBUTING.md +105 -0
- data/factory_bot-6.6.0/GETTING_STARTED.md +2203 -0
- data/factory_bot-6.6.0/LICENSE +19 -0
- data/factory_bot-6.6.0/NAME.md +18 -0
- data/factory_bot-6.6.0/NEWS.md +607 -0
- data/factory_bot-6.6.0/README.md +99 -0
- data/factory_bot-6.6.0/lib/factory_bot/aliases.rb +18 -0
- data/factory_bot-6.6.0/lib/factory_bot/attribute/association.rb +27 -0
- data/factory_bot-6.6.0/lib/factory_bot/attribute/dynamic.rb +25 -0
- data/factory_bot-6.6.0/lib/factory_bot/attribute/sequence.rb +16 -0
- data/factory_bot-6.6.0/lib/factory_bot/attribute.rb +27 -0
- data/factory_bot-6.6.0/lib/factory_bot/attribute_assigner.rb +168 -0
- data/factory_bot-6.6.0/lib/factory_bot/attribute_list.rb +68 -0
- data/factory_bot-6.6.0/lib/factory_bot/callback.rb +33 -0
- data/factory_bot-6.6.0/lib/factory_bot/callbacks_observer.rb +39 -0
- data/factory_bot-6.6.0/lib/factory_bot/configuration.rb +33 -0
- data/factory_bot-6.6.0/lib/factory_bot/declaration/association.rb +58 -0
- data/factory_bot-6.6.0/lib/factory_bot/declaration/dynamic.rb +28 -0
- data/factory_bot-6.6.0/lib/factory_bot/declaration/implicit.rb +38 -0
- data/factory_bot-6.6.0/lib/factory_bot/declaration.rb +23 -0
- data/factory_bot-6.6.0/lib/factory_bot/declaration_list.rb +49 -0
- data/factory_bot-6.6.0/lib/factory_bot/decorator/attribute_hash.rb +16 -0
- data/factory_bot-6.6.0/lib/factory_bot/decorator/disallows_duplicates_registry.rb +13 -0
- data/factory_bot-6.6.0/lib/factory_bot/decorator/invocation_tracker.rb +20 -0
- data/factory_bot-6.6.0/lib/factory_bot/decorator/new_constructor.rb +12 -0
- data/factory_bot-6.6.0/lib/factory_bot/decorator.rb +25 -0
- data/factory_bot-6.6.0/lib/factory_bot/definition.rb +210 -0
- data/factory_bot-6.6.0/lib/factory_bot/definition_hierarchy.rb +38 -0
- data/factory_bot-6.6.0/lib/factory_bot/definition_proxy.rb +269 -0
- data/factory_bot-6.6.0/lib/factory_bot/enum.rb +27 -0
- data/factory_bot-6.6.0/lib/factory_bot/errors.rb +28 -0
- data/factory_bot-6.6.0/lib/factory_bot/evaluation.rb +23 -0
- data/factory_bot-6.6.0/lib/factory_bot/evaluator.rb +86 -0
- data/factory_bot-6.6.0/lib/factory_bot/evaluator_class_definer.rb +20 -0
- data/factory_bot-6.6.0/lib/factory_bot/factory.rb +178 -0
- data/factory_bot-6.6.0/lib/factory_bot/factory_runner.rb +35 -0
- data/factory_bot-6.6.0/lib/factory_bot/find_definitions.rb +25 -0
- data/factory_bot-6.6.0/lib/factory_bot/internal.rb +124 -0
- data/factory_bot-6.6.0/lib/factory_bot/linter.rb +121 -0
- data/factory_bot-6.6.0/lib/factory_bot/null_factory.rb +27 -0
- data/factory_bot-6.6.0/lib/factory_bot/null_object.rb +20 -0
- data/factory_bot-6.6.0/lib/factory_bot/registry.rb +59 -0
- data/factory_bot-6.6.0/lib/factory_bot/reload.rb +7 -0
- data/factory_bot-6.6.0/lib/factory_bot/sequence.rb +197 -0
- data/factory_bot-6.6.0/lib/factory_bot/strategy/attributes_for.rb +17 -0
- data/factory_bot-6.6.0/lib/factory_bot/strategy/build.rb +21 -0
- data/factory_bot-6.6.0/lib/factory_bot/strategy/create.rb +24 -0
- data/factory_bot-6.6.0/lib/factory_bot/strategy/null.rb +15 -0
- data/factory_bot-6.6.0/lib/factory_bot/strategy/stub.rb +129 -0
- data/factory_bot-6.6.0/lib/factory_bot/strategy.rb +15 -0
- data/factory_bot-6.6.0/lib/factory_bot/strategy_syntax_method_registrar.rb +65 -0
- data/factory_bot-6.6.0/lib/factory_bot/syntax/default.rb +64 -0
- data/factory_bot-6.6.0/lib/factory_bot/syntax/methods.rb +181 -0
- data/factory_bot-6.6.0/lib/factory_bot/syntax.rb +7 -0
- data/factory_bot-6.6.0/lib/factory_bot/syntax_runner.rb +6 -0
- data/factory_bot-6.6.0/lib/factory_bot/trait.rb +39 -0
- data/factory_bot-6.6.0/lib/factory_bot/uri_manager.rb +63 -0
- data/factory_bot-6.6.0/lib/factory_bot/version.rb +3 -0
- data/factory_bot-6.6.0/lib/factory_bot.rb +117 -0
- data/tiny-lite-mod.gemspec +12 -0
- metadata +101 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# factory_bot
|
|
2
|
+
|
|
3
|
+
[![Build Status][ci-image]][ci] [![Gem Version][version-image]][version]
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
If you want to use factory_bot with Rails, see
|
|
8
|
+
[factory_bot_rails](https://github.com/thoughtbot/factory_bot_rails).
|
|
9
|
+
|
|
10
|
+
Interested in the history of the project name? You can find the history [here](https://github.com/thoughtbot/factory_bot/blob/main/NAME.md)
|
|
11
|
+
|
|
12
|
+
Transitioning from factory\_girl? Check out the [upgrade guide](https://github.com/thoughtbot/factory_bot/blob/v4.9.0/UPGRADE_FROM_FACTORY_GIRL.md).
|
|
13
|
+
|
|
14
|
+
## Documentation
|
|
15
|
+
|
|
16
|
+
See our extensive reference, guides, and cookbook in [the factory_bot book][].
|
|
17
|
+
|
|
18
|
+
For information on integrations with third party libraries, such as RSpec or
|
|
19
|
+
Rails, see [the factory_bot wiki][].
|
|
20
|
+
|
|
21
|
+
We also have [a detailed introductory video][], available for free on Upcase.
|
|
22
|
+
|
|
23
|
+
[a detailed introductory video]: https://upcase.com/videos/factory-bot?utm_source=github&utm_medium=open-source&utm_campaign=factory-girl
|
|
24
|
+
[the factory_bot book]: https://thoughtbot.github.io/factory_bot
|
|
25
|
+
[the factory_bot wiki]: https://github.com/thoughtbot/factory_bot/wiki
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
Run:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
bundle add factory_bot
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To install the gem manually from your shell, run:
|
|
36
|
+
|
|
37
|
+
```shell
|
|
38
|
+
gem install factory_bot
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Supported Ruby versions
|
|
42
|
+
|
|
43
|
+
Supported Ruby versions are listed in `.github/workflows/build.yml` ([source](https://github.com/thoughtbot/factory_bot/blob/main/.github/workflows/build.yml))
|
|
44
|
+
|
|
45
|
+
## More Information
|
|
46
|
+
|
|
47
|
+
* [Rubygems](https://rubygems.org/gems/factory_bot)
|
|
48
|
+
* [Stack Overflow](https://stackoverflow.com/questions/tagged/factory-bot)
|
|
49
|
+
* [Issues](https://github.com/thoughtbot/factory_bot/issues)
|
|
50
|
+
* [GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS](https://robots.thoughtbot.com/)
|
|
51
|
+
|
|
52
|
+
[GETTING_STARTED]: https://github.com/thoughtbot/factory_bot/blob/main/GETTING_STARTED.md
|
|
53
|
+
|
|
54
|
+
## Useful Tools
|
|
55
|
+
|
|
56
|
+
* [FactoryTrace](https://github.com/djezzzl/factory_trace) - helps to find unused factories and traits.
|
|
57
|
+
* [ruby-lsp-factory_bot](https://github.com/donny741/ruby-lsp-factory_bot) / [ruby-lsp-rails-factory-bot](https://github.com/johansenja/ruby-lsp-rails-factory-bot) - integration with [ruby-lsp](https://github.com/Shopify/ruby-lsp) to provide intellisense
|
|
58
|
+
|
|
59
|
+
Contributing
|
|
60
|
+
------------
|
|
61
|
+
|
|
62
|
+
Please see [CONTRIBUTING.md](https://github.com/thoughtbot/factory_bot/blob/main/CONTRIBUTING.md).
|
|
63
|
+
|
|
64
|
+
factory_bot was originally written by Joe Ferris and is maintained by thoughtbot.
|
|
65
|
+
Many improvements and bugfixes were contributed by the [open source
|
|
66
|
+
community](https://github.com/thoughtbot/factory_bot/graphs/contributors).
|
|
67
|
+
|
|
68
|
+
License
|
|
69
|
+
-------
|
|
70
|
+
|
|
71
|
+
factory_bot is Copyright © 2008 Joe Ferris and thoughtbot. It is free
|
|
72
|
+
software, and may be redistributed under the terms specified in the
|
|
73
|
+
[LICENSE] file.
|
|
74
|
+
|
|
75
|
+
[LICENSE]: https://github.com/thoughtbot/factory_bot/blob/main/LICENSE
|
|
76
|
+
|
|
77
|
+
<!-- START /templates/footer.md -->
|
|
78
|
+
## About thoughtbot
|
|
79
|
+
|
|
80
|
+

|
|
81
|
+
|
|
82
|
+
This repo is maintained and funded by thoughtbot, inc.
|
|
83
|
+
The names and logos for thoughtbot are trademarks of thoughtbot, inc.
|
|
84
|
+
|
|
85
|
+
We love open source software!
|
|
86
|
+
See [our other projects][community].
|
|
87
|
+
We are [available for hire][hire].
|
|
88
|
+
|
|
89
|
+
[community]: https://thoughtbot.com/community?utm_source=github
|
|
90
|
+
[hire]: https://thoughtbot.com/hire-us?utm_source=github
|
|
91
|
+
|
|
92
|
+
<!-- END /templates/footer.md -->
|
|
93
|
+
|
|
94
|
+
[ci-image]: https://github.com/thoughtbot/factory_bot/actions/workflows/build.yml/badge.svg?branch=main
|
|
95
|
+
[ci]: https://github.com/thoughtbot/factory_bot/actions?query=workflow%3ABuild+branch%3Amain
|
|
96
|
+
[version-image]: https://badge.fury.io/rb/factory_bot.svg
|
|
97
|
+
[version]: https://badge.fury.io/rb/factory_bot
|
|
98
|
+
[hound-badge-image]: https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg
|
|
99
|
+
[hound]: https://houndci.com
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module FactoryBot
|
|
2
|
+
class << self
|
|
3
|
+
attr_accessor :aliases
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
self.aliases = [
|
|
7
|
+
[/(.+)_id/, '\1'],
|
|
8
|
+
[/(.*)/, '\1_id']
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
def self.aliases_for(attribute)
|
|
12
|
+
aliases.map { |(pattern, replace)|
|
|
13
|
+
if pattern.match?(attribute)
|
|
14
|
+
attribute.to_s.sub(pattern, replace).to_sym
|
|
15
|
+
end
|
|
16
|
+
}.compact << attribute
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module FactoryBot
|
|
2
|
+
class Attribute
|
|
3
|
+
# @api private
|
|
4
|
+
class Association < Attribute
|
|
5
|
+
attr_reader :factory
|
|
6
|
+
|
|
7
|
+
def initialize(name, factory, overrides)
|
|
8
|
+
super(name, false)
|
|
9
|
+
@factory = factory
|
|
10
|
+
@overrides = overrides
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_proc
|
|
14
|
+
factory = @factory
|
|
15
|
+
overrides = @overrides
|
|
16
|
+
traits_and_overrides = [factory, overrides].flatten
|
|
17
|
+
factory_name = traits_and_overrides.shift
|
|
18
|
+
|
|
19
|
+
-> { association(factory_name, *traits_and_overrides) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def association?
|
|
23
|
+
true
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module FactoryBot
|
|
2
|
+
class Attribute
|
|
3
|
+
# @api private
|
|
4
|
+
class Dynamic < Attribute
|
|
5
|
+
def initialize(name, ignored, block)
|
|
6
|
+
super(name, ignored)
|
|
7
|
+
@block = block
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def to_proc
|
|
11
|
+
block = @block
|
|
12
|
+
|
|
13
|
+
-> {
|
|
14
|
+
value = case block.arity
|
|
15
|
+
when 1, -1, -2 then instance_exec(self, &block)
|
|
16
|
+
else instance_exec(&block)
|
|
17
|
+
end
|
|
18
|
+
raise SequenceAbuseError if FactoryBot::Sequence === value
|
|
19
|
+
|
|
20
|
+
value
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module FactoryBot
|
|
2
|
+
class Attribute
|
|
3
|
+
# @api private
|
|
4
|
+
class Sequence < Attribute
|
|
5
|
+
def initialize(name, sequence, ignored)
|
|
6
|
+
super(name, ignored)
|
|
7
|
+
@sequence = sequence
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def to_proc
|
|
11
|
+
sequence = @sequence
|
|
12
|
+
-> { FactoryBot.generate(sequence) }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require "factory_bot/attribute/dynamic"
|
|
2
|
+
require "factory_bot/attribute/association"
|
|
3
|
+
require "factory_bot/attribute/sequence"
|
|
4
|
+
|
|
5
|
+
module FactoryBot
|
|
6
|
+
# @api private
|
|
7
|
+
class Attribute
|
|
8
|
+
attr_reader :name, :ignored
|
|
9
|
+
|
|
10
|
+
def initialize(name, ignored)
|
|
11
|
+
@name = name.to_sym
|
|
12
|
+
@ignored = ignored
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_proc
|
|
16
|
+
-> {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def association?
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def alias_for?(attr)
|
|
24
|
+
FactoryBot.aliases_for(attr).include?(name)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
module FactoryBot
|
|
2
|
+
# @api private
|
|
3
|
+
class AttributeAssigner
|
|
4
|
+
def initialize(evaluator, build_class, &instance_builder)
|
|
5
|
+
@build_class = build_class
|
|
6
|
+
@instance_builder = instance_builder
|
|
7
|
+
@evaluator = evaluator
|
|
8
|
+
@attribute_list = evaluator.class.attribute_list
|
|
9
|
+
@attribute_names_assigned = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# constructs an object-based factory product
|
|
13
|
+
def object
|
|
14
|
+
@evaluator.instance = build_class_instance
|
|
15
|
+
build_class_instance.tap do |instance|
|
|
16
|
+
attributes_to_set_on_instance.each do |attribute|
|
|
17
|
+
instance.public_send(:"#{attribute}=", get(attribute))
|
|
18
|
+
@attribute_names_assigned << attribute
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# constructs a Hash-based factory product
|
|
24
|
+
def hash
|
|
25
|
+
@evaluator.instance = build_hash
|
|
26
|
+
|
|
27
|
+
attributes_to_set_on_hash.each_with_object({}) do |attribute, result|
|
|
28
|
+
result[attribute] = get(attribute)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
# Track evaluation of methods on the evaluator to prevent the duplicate
|
|
35
|
+
# assignment of attributes accessed and via `initialize_with` syntax
|
|
36
|
+
def method_tracking_evaluator
|
|
37
|
+
@method_tracking_evaluator ||= Decorator::AttributeHash.new(
|
|
38
|
+
decorated_evaluator,
|
|
39
|
+
attribute_names_to_assign
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def decorated_evaluator
|
|
44
|
+
Decorator::NewConstructor.new(
|
|
45
|
+
Decorator::InvocationTracker.new(@evaluator),
|
|
46
|
+
@build_class
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def methods_invoked_on_evaluator
|
|
51
|
+
method_tracking_evaluator.__invoked_methods__
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def build_class_instance
|
|
55
|
+
@build_class_instance ||= method_tracking_evaluator.instance_exec(&@instance_builder)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def build_hash
|
|
59
|
+
@build_hash ||= NullObject.new(hash_instance_methods_to_respond_to)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def get(attribute_name)
|
|
63
|
+
@evaluator.send(attribute_name)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def attributes_to_set_on_instance
|
|
67
|
+
(attribute_names_to_assign - @attribute_names_assigned - methods_invoked_on_evaluator).uniq
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def attributes_to_set_on_hash
|
|
71
|
+
attribute_names_to_assign - association_names
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Builds a list of attributes names that should be assigned to the factory product
|
|
75
|
+
def attribute_names_to_assign
|
|
76
|
+
@attribute_names_to_assign ||= begin
|
|
77
|
+
# start a list of candidates containing non-transient attributes and overrides
|
|
78
|
+
assignment_candidates = non_ignored_attribute_names + override_names
|
|
79
|
+
# then remove any transient attributes (potentially reintroduced by the overrides),
|
|
80
|
+
# and remove ignorable aliased attributes from the candidate list
|
|
81
|
+
assignment_candidates - ignored_attribute_names - attribute_names_overriden_by_alias
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def non_ignored_attribute_names
|
|
86
|
+
@attribute_list.non_ignored.names
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def ignored_attribute_names
|
|
90
|
+
@attribute_list.ignored.names
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def association_names
|
|
94
|
+
@attribute_list.associations.names
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def override_names
|
|
98
|
+
@evaluator.__override_names__
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def attribute_names
|
|
102
|
+
@attribute_list.names
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def hash_instance_methods_to_respond_to
|
|
106
|
+
attribute_names + override_names + @build_class.instance_methods
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Builds a list of attribute names which are slated to be interrupted by an override.
|
|
110
|
+
def attribute_names_overriden_by_alias
|
|
111
|
+
@attribute_list
|
|
112
|
+
.non_ignored
|
|
113
|
+
.flat_map { |attribute|
|
|
114
|
+
override_names.map do |override|
|
|
115
|
+
attribute.name if ignorable_alias?(attribute, override)
|
|
116
|
+
end
|
|
117
|
+
}
|
|
118
|
+
.compact
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Is the attribute an ignorable alias of the override?
|
|
122
|
+
# An attribute is ignorable when it is an alias of the override AND it is
|
|
123
|
+
# either interrupting an assocciation OR is not the name of another attribute
|
|
124
|
+
#
|
|
125
|
+
# @note An "alias" is currently an overloaded term for two distinct cases:
|
|
126
|
+
# (1) attributes which are aliases and reference the same value
|
|
127
|
+
# (2) a logical grouping of a foreign key and an associated object
|
|
128
|
+
def ignorable_alias?(attribute, override)
|
|
129
|
+
return false unless attribute.alias_for?(override)
|
|
130
|
+
|
|
131
|
+
# The attribute alias should be ignored when the override interrupts an association
|
|
132
|
+
return true if override_interrupts_association?(attribute, override)
|
|
133
|
+
|
|
134
|
+
# Remaining aliases should be ignored when the override does not match a declared attribute.
|
|
135
|
+
# An override which is an alias to a declared attribute should not interrupt the aliased
|
|
136
|
+
# attribute and interrupt only the attribute with a matching name. This workaround allows a
|
|
137
|
+
# factory to declare both <attribute> and <attribute>_id as separate and distinct attributes.
|
|
138
|
+
!override_matches_declared_attribute?(override)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Does this override interrupt an association?
|
|
142
|
+
# When true, this indicates the aliased attribute is related to a declared association and the
|
|
143
|
+
# override does not match the attribute name.
|
|
144
|
+
#
|
|
145
|
+
# @note Association overrides should take precedence over a declared foreign key attribute.
|
|
146
|
+
#
|
|
147
|
+
# @note An override may interrupt an association by providing the associated object or
|
|
148
|
+
# by providing the foreign key.
|
|
149
|
+
#
|
|
150
|
+
# @param [FactoryBot::Attribute] aliased_attribute
|
|
151
|
+
# @param [Symbol] override name of an override which is an alias to the attribute name
|
|
152
|
+
def override_interrupts_association?(aliased_attribute, override)
|
|
153
|
+
(aliased_attribute.association? || association_names.include?(override)) &&
|
|
154
|
+
aliased_attribute.name != override
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Does this override match the name of any declared attribute?
|
|
158
|
+
#
|
|
159
|
+
# @note Checking against the names of all attributes, resolves any issues with having both
|
|
160
|
+
# <attribute> and <attribute>_id in the same factory. This also takes into account ignored
|
|
161
|
+
# attributes that should not be assigned (aka transient attributes)
|
|
162
|
+
#
|
|
163
|
+
# @param [Symbol] override the name of an override
|
|
164
|
+
def override_matches_declared_attribute?(override)
|
|
165
|
+
attribute_names.include?(override)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module FactoryBot
|
|
2
|
+
# @api private
|
|
3
|
+
class AttributeList
|
|
4
|
+
include Enumerable
|
|
5
|
+
|
|
6
|
+
def initialize(name = nil, attributes = [])
|
|
7
|
+
@name = name
|
|
8
|
+
@attributes = attributes
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def define_attribute(attribute)
|
|
12
|
+
ensure_attribute_not_self_referencing! attribute
|
|
13
|
+
ensure_attribute_not_defined! attribute
|
|
14
|
+
|
|
15
|
+
add_attribute attribute
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def each(&block)
|
|
19
|
+
@attributes.each(&block)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def names
|
|
23
|
+
map(&:name)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def associations
|
|
27
|
+
AttributeList.new(@name, select(&:association?))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def ignored
|
|
31
|
+
AttributeList.new(@name, select(&:ignored))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def non_ignored
|
|
35
|
+
AttributeList.new(@name, reject(&:ignored))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def apply_attributes(attributes_to_apply)
|
|
39
|
+
attributes_to_apply.each { |attribute| add_attribute(attribute) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def add_attribute(attribute)
|
|
45
|
+
@attributes << attribute
|
|
46
|
+
attribute
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def ensure_attribute_not_defined!(attribute)
|
|
50
|
+
if attribute_defined?(attribute.name)
|
|
51
|
+
raise AttributeDefinitionError, "Attribute already defined: #{attribute.name}"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def ensure_attribute_not_self_referencing!(attribute)
|
|
56
|
+
if attribute.respond_to?(:factory) && attribute.factory == @name
|
|
57
|
+
message = "Self-referencing association '#{attribute.name}' in '#{attribute.factory}'"
|
|
58
|
+
raise AssociationDefinitionError, message
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def attribute_defined?(attribute_name)
|
|
63
|
+
@attributes.any? do |attribute|
|
|
64
|
+
attribute.name == attribute_name
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module FactoryBot
|
|
2
|
+
class Callback
|
|
3
|
+
attr_reader :name
|
|
4
|
+
|
|
5
|
+
def initialize(name, block)
|
|
6
|
+
@name = name.to_sym
|
|
7
|
+
@block = block
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run(instance, evaluator)
|
|
11
|
+
case block.arity
|
|
12
|
+
when 1, -1, -2 then syntax_runner.instance_exec(instance, &block)
|
|
13
|
+
when 2 then syntax_runner.instance_exec(instance, evaluator, &block)
|
|
14
|
+
else syntax_runner.instance_exec(&block)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def ==(other)
|
|
19
|
+
name == other.name &&
|
|
20
|
+
block == other.block
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
protected
|
|
24
|
+
|
|
25
|
+
attr_reader :block
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def syntax_runner
|
|
30
|
+
@syntax_runner ||= SyntaxRunner.new
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module FactoryBot
|
|
2
|
+
# @api private
|
|
3
|
+
class CallbacksObserver
|
|
4
|
+
def initialize(callbacks, evaluator)
|
|
5
|
+
@callbacks = callbacks
|
|
6
|
+
@evaluator = evaluator
|
|
7
|
+
@completed = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def update(name, result_instance)
|
|
11
|
+
callbacks_by_name(name).each do |callback|
|
|
12
|
+
if !completed?(result_instance, callback)
|
|
13
|
+
callback.run(result_instance, @evaluator)
|
|
14
|
+
record_completion!(result_instance, callback)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def callbacks_by_name(name)
|
|
22
|
+
@callbacks.select { |callback| callback.name == name }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def completed?(instance, callback)
|
|
26
|
+
key = completion_key_for(instance, callback)
|
|
27
|
+
@completed.include?(key)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def record_completion!(instance, callback)
|
|
31
|
+
key = completion_key_for(instance, callback)
|
|
32
|
+
@completed << key
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def completion_key_for(instance, callback)
|
|
36
|
+
"#{instance.object_id}-#{callback.object_id}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module FactoryBot
|
|
2
|
+
# @api private
|
|
3
|
+
class Configuration
|
|
4
|
+
attr_reader(
|
|
5
|
+
:callback_names,
|
|
6
|
+
:factories,
|
|
7
|
+
:inline_sequences,
|
|
8
|
+
:sequences,
|
|
9
|
+
:strategies,
|
|
10
|
+
:traits
|
|
11
|
+
)
|
|
12
|
+
|
|
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")
|
|
18
|
+
@callback_names = Set.new
|
|
19
|
+
@definition = Definition.new(:configuration)
|
|
20
|
+
@inline_sequences = []
|
|
21
|
+
|
|
22
|
+
to_create(&:save!)
|
|
23
|
+
initialize_with { new }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
delegate :to_create, :skip_create, :constructor, :before, :after,
|
|
27
|
+
:callback, :callbacks, to: :@definition
|
|
28
|
+
|
|
29
|
+
def initialize_with(&block)
|
|
30
|
+
@definition.define_constructor(&block)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module FactoryBot
|
|
2
|
+
class Declaration
|
|
3
|
+
# @api private
|
|
4
|
+
class Association < Declaration
|
|
5
|
+
def initialize(name, *options)
|
|
6
|
+
super(name, false)
|
|
7
|
+
@options = options.dup
|
|
8
|
+
@overrides = options.extract_options!
|
|
9
|
+
@factory_name = @overrides.delete(:factory) || name
|
|
10
|
+
@traits = options
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ==(other)
|
|
14
|
+
self.class == other.class &&
|
|
15
|
+
name == other.name &&
|
|
16
|
+
options == other.options
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
protected
|
|
20
|
+
|
|
21
|
+
attr_reader :options
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
attr_reader :factory_name, :overrides, :traits
|
|
26
|
+
|
|
27
|
+
def build
|
|
28
|
+
raise_if_arguments_are_declarations!
|
|
29
|
+
|
|
30
|
+
[
|
|
31
|
+
Attribute::Association.new(
|
|
32
|
+
name,
|
|
33
|
+
factory_name,
|
|
34
|
+
[traits, overrides].flatten
|
|
35
|
+
)
|
|
36
|
+
]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def raise_if_arguments_are_declarations!
|
|
40
|
+
if factory_name.is_a?(Declaration)
|
|
41
|
+
raise ArgumentError.new(<<~MSG)
|
|
42
|
+
Association '#{name}' received an invalid factory argument.
|
|
43
|
+
Did you mean? 'factory: :#{factory_name.name}'
|
|
44
|
+
MSG
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
overrides.each do |attribute, value|
|
|
48
|
+
if value.is_a?(Declaration)
|
|
49
|
+
raise ArgumentError.new(<<~MSG)
|
|
50
|
+
Association '#{name}' received an invalid attribute override.
|
|
51
|
+
Did you mean? '#{attribute}: :#{value.name}'
|
|
52
|
+
MSG
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module FactoryBot
|
|
2
|
+
class Declaration
|
|
3
|
+
# @api private
|
|
4
|
+
class Dynamic < Declaration
|
|
5
|
+
def initialize(name, ignored = false, block = nil)
|
|
6
|
+
super(name, ignored)
|
|
7
|
+
@block = block
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def ==(other)
|
|
11
|
+
self.class == other.class &&
|
|
12
|
+
name == other.name &&
|
|
13
|
+
ignored == other.ignored &&
|
|
14
|
+
block == other.block
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
protected
|
|
18
|
+
|
|
19
|
+
attr_reader :block
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def build
|
|
24
|
+
[Attribute::Dynamic.new(name, @ignored, @block)]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module FactoryBot
|
|
2
|
+
class Declaration
|
|
3
|
+
# @api private
|
|
4
|
+
class Implicit < Declaration
|
|
5
|
+
def initialize(name, factory = nil, ignored = false)
|
|
6
|
+
super(name, ignored)
|
|
7
|
+
@factory = factory
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def ==(other)
|
|
11
|
+
self.class == other.class &&
|
|
12
|
+
name == other.name &&
|
|
13
|
+
factory == other.factory &&
|
|
14
|
+
ignored == other.ignored
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
protected
|
|
18
|
+
|
|
19
|
+
attr_reader :factory
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def build
|
|
24
|
+
if FactoryBot.factories.registered?(name)
|
|
25
|
+
[Attribute::Association.new(name, name, {})]
|
|
26
|
+
elsif FactoryBot::Internal.sequences.registered?(name)
|
|
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
|
|
31
|
+
else
|
|
32
|
+
@factory.inherit_traits([name])
|
|
33
|
+
[]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|