fabrication 3.0.0.beta.1.1 → 3.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.
- checksums.yaml +4 -4
- data/README.markdown +2 -2
- data/lib/fabricate.rb +23 -17
- data/lib/fabrication/config.rb +42 -5
- data/lib/fabrication/cucumber/step_fabricator.rb +15 -7
- data/lib/fabrication/generator/active_record.rb +5 -5
- data/lib/fabrication/generator/base.rb +42 -21
- data/lib/fabrication/generator/mongoid.rb +17 -0
- data/lib/fabrication/generator/sequel.rb +21 -9
- data/lib/fabrication/schematic/attribute.rb +3 -3
- data/lib/fabrication/schematic/definition.rb +17 -16
- data/lib/fabrication/schematic/evaluator.rb +2 -2
- data/lib/fabrication/schematic/manager.rb +11 -9
- data/lib/fabrication/schematic/runner.rb +3 -3
- data/lib/fabrication/sequencer.rb +19 -6
- data/lib/fabrication/support.rb +52 -57
- data/lib/fabrication/syntax/make.rb +4 -4
- data/lib/fabrication/transform.rb +18 -16
- data/lib/fabrication/version.rb +1 -1
- data/lib/fabrication.rb +15 -11
- data/lib/rails/generators/fabrication/cucumber_steps/cucumber_steps_generator.rb +1 -1
- data/lib/rails/generators/fabrication/cucumber_steps/templates/fabrication_steps.rb +7 -5
- data/lib/rails/generators/fabrication/model/model_generator.rb +3 -1
- metadata +19 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2d9e7e01f4fcab4cb03ccb401f16b62a5aa854e132627292a0dfaa6e57ede61
|
4
|
+
data.tar.gz: a5ef27ece35d6d35a06bcf341b001c06aa9bc1c5270d6305c7ad01344fc5cc21
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 916b51d8df3521c9423be89cb39e975a9d5e0f7a94d10b42b9c88599aa9b8a8e14163d149da0c231b87f950e247350f7177f157dcdce748bab3226d1db1a4a91
|
7
|
+
data.tar.gz: 167c0c2ab653376eb59e7217bf1e32c766284e9a776090738345e5f89695bc87c558129ad20ed9fc66b053b446c2d3dcdf3a3f296461f81860be43db23f7ada8
|
data/README.markdown
CHANGED
@@ -12,6 +12,6 @@ Fabrication is tested against all officially supported versions of Ruby.
|
|
12
12
|
|
13
13
|
Please see the Fabrication website for up-to-date documentation: http://fabricationgem.org
|
14
14
|
|
15
|
-
You can also view the raw documentation without all the awesome: https://
|
15
|
+
You can also view the raw documentation without all the awesome: https://gitlab.com/fabrication-gem/docsite/-/blob/master/source/index.html.markdown
|
16
16
|
|
17
|
-
Get help
|
17
|
+
Get help by opening an issue: https://gitlab.com/fabrication-gem/fabrication/-/issues
|
data/lib/fabricate.rb
CHANGED
@@ -1,40 +1,46 @@
|
|
1
1
|
class Fabricate
|
2
|
-
def self.times(count, name, overrides = {}, &
|
3
|
-
count.
|
2
|
+
def self.times(count, name, overrides = {}, &)
|
3
|
+
Array.new(count).map { Fabricate(name, overrides, &) }
|
4
4
|
end
|
5
5
|
|
6
|
-
def self.build_times(count, name, overrides = {}, &
|
7
|
-
count.
|
6
|
+
def self.build_times(count, name, overrides = {}, &)
|
7
|
+
Array.new(count).map { Fabricate.build(name, overrides, &) }
|
8
8
|
end
|
9
9
|
|
10
|
-
def self.attributes_for_times(count, name, overrides = {}, &
|
11
|
-
count.
|
10
|
+
def self.attributes_for_times(count, name, overrides = {}, &)
|
11
|
+
Array.new(count).map { Fabricate.attributes_for(name, overrides, &) }
|
12
12
|
end
|
13
13
|
|
14
|
-
def self.attributes_for(name, overrides = {}, &
|
14
|
+
def self.attributes_for(name, overrides = {}, &)
|
15
15
|
fail_if_initializing(name)
|
16
|
-
schematic(name).to_attributes(overrides, &
|
16
|
+
schematic(name).to_attributes(overrides, &)
|
17
17
|
end
|
18
18
|
|
19
|
-
def self.to_params(name, overrides = {}, &
|
19
|
+
def self.to_params(name, overrides = {}, &)
|
20
20
|
fail_if_initializing(name)
|
21
|
-
schematic(name).to_params(overrides, &
|
21
|
+
schematic(name).to_params(overrides, &)
|
22
22
|
end
|
23
23
|
|
24
|
-
def self.build(name, overrides = {}, &
|
24
|
+
def self.build(name, overrides = {}, &)
|
25
25
|
fail_if_initializing(name)
|
26
|
-
schematic(name).build(overrides, &
|
27
|
-
Fabrication::
|
26
|
+
schematic(name).build(overrides, &).tap do |object|
|
27
|
+
Fabrication::Config.notifiers.each do |notifier|
|
28
|
+
notifier.call(name, object)
|
29
|
+
end
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
|
-
def self.create(name, overrides = {}, &
|
33
|
+
def self.create(name, overrides = {}, &)
|
32
34
|
fail_if_initializing(name)
|
33
|
-
schematic(name).fabricate(overrides, &
|
35
|
+
schematic(name).fabricate(overrides, &).tap do |object|
|
36
|
+
Fabrication::Config.notifiers.each do |notifier|
|
37
|
+
notifier.call(name, object)
|
38
|
+
end
|
39
|
+
end
|
34
40
|
end
|
35
41
|
|
36
|
-
def self.sequence(name = Fabrication::Sequencer::DEFAULT, start = nil, &
|
37
|
-
Fabrication::Sequencer.sequence(name, start, &
|
42
|
+
def self.sequence(name = Fabrication::Sequencer::DEFAULT, start = nil, &)
|
43
|
+
Fabrication::Sequencer.sequence(name, start, &)
|
38
44
|
end
|
39
45
|
|
40
46
|
def self.schematic(name)
|
data/lib/fabrication/config.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
1
3
|
module Fabrication
|
2
4
|
module Config
|
3
5
|
extend self
|
@@ -14,16 +16,34 @@ module Fabrication
|
|
14
16
|
nil
|
15
17
|
end
|
16
18
|
|
19
|
+
attr_writer :logger, :sequence_start
|
20
|
+
|
21
|
+
def logger
|
22
|
+
@logger ||= Logger.new($stdout).tap do |logger|
|
23
|
+
logger.level = Logger::WARN
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
17
27
|
def fabricator_path
|
18
28
|
@fabricator_path ||= ['test/fabricators', 'spec/fabricators']
|
19
29
|
end
|
20
30
|
alias fabricator_paths fabricator_path
|
21
31
|
|
32
|
+
def fabricator_dir
|
33
|
+
Support.log_deprecation('Fabrication::Config.fabricator_dir has been ' \
|
34
|
+
'replaced by Fabrication::Config.fabricator_path')
|
35
|
+
fabricator_path
|
36
|
+
end
|
37
|
+
|
22
38
|
def fabricator_path=(folders)
|
23
39
|
@fabricator_path = ([] << folders).flatten
|
24
40
|
end
|
25
41
|
|
26
|
-
|
42
|
+
def fabricator_dir=(folders)
|
43
|
+
Support.log_deprecation('Fabrication::Config.fabricator_dir has been ' \
|
44
|
+
'replaced by Fabrication::Config.fabricator_path')
|
45
|
+
self.fabricator_path = folders
|
46
|
+
end
|
27
47
|
|
28
48
|
def sequence_start
|
29
49
|
@sequence_start ||= 0
|
@@ -38,10 +58,6 @@ module Fabrication
|
|
38
58
|
end
|
39
59
|
alias path_prefixes path_prefix
|
40
60
|
|
41
|
-
def register_with_steps?
|
42
|
-
@register_with_steps ||= nil
|
43
|
-
end
|
44
|
-
|
45
61
|
def generators
|
46
62
|
@generators ||= []
|
47
63
|
end
|
@@ -57,5 +73,26 @@ module Fabrication
|
|
57
73
|
def recursion_limit=(limit)
|
58
74
|
@recursion_limit = limit
|
59
75
|
end
|
76
|
+
|
77
|
+
def register_with_steps=(value)
|
78
|
+
Support.log_deprecation(
|
79
|
+
'Fabrication::Config.register_with_steps has been deprecated. ' \
|
80
|
+
'Please regenerate your cucumber steps with `rails g fabrication:cucumber_steps'
|
81
|
+
)
|
82
|
+
|
83
|
+
return unless value
|
84
|
+
|
85
|
+
register_notifier do |name, object|
|
86
|
+
Fabrication::Cucumber::Fabrications[name] = object
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def notifiers
|
91
|
+
@notifiers ||= []
|
92
|
+
end
|
93
|
+
|
94
|
+
def register_notifier(&block)
|
95
|
+
notifiers.push(block)
|
96
|
+
end
|
60
97
|
end
|
61
98
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
1
3
|
module Fabrication
|
2
4
|
module Cucumber
|
3
5
|
class StepFabricator
|
@@ -20,7 +22,7 @@ module Fabrication
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def n(count, attrs = {})
|
23
|
-
count.
|
25
|
+
Array.new(count).map { make(attrs) }.tap { |o| remember(o) }
|
24
26
|
end
|
25
27
|
|
26
28
|
# rubocop:disable Naming/PredicateName
|
@@ -28,7 +30,7 @@ module Fabrication
|
|
28
30
|
instance = Fabrications[@fabricator]
|
29
31
|
children = dehumanize(children)
|
30
32
|
[Fabrications[children]].flatten.each do |child|
|
31
|
-
child.send("#{klass.to_s.underscore.downcase}=", instance)
|
33
|
+
child.send(:"#{klass.to_s.underscore.downcase}=", instance)
|
32
34
|
child.respond_to?(:save!) && child.save!
|
33
35
|
end
|
34
36
|
end
|
@@ -76,7 +78,7 @@ module Fabrication
|
|
76
78
|
parent_class_name = parent.class.to_s.underscore
|
77
79
|
|
78
80
|
parent_instance = parent
|
79
|
-
unless klass.new.respond_to?("#{parent_class_name}=")
|
81
|
+
unless klass.new.respond_to?(:"#{parent_class_name}=")
|
80
82
|
parent_class_name = parent_class_name.pluralize
|
81
83
|
parent_instance = [parent]
|
82
84
|
end
|
@@ -85,17 +87,23 @@ module Fabrication
|
|
85
87
|
end
|
86
88
|
end
|
87
89
|
|
88
|
-
|
90
|
+
class Fabrications
|
91
|
+
include Singleton
|
92
|
+
|
89
93
|
def self.fabrications
|
90
|
-
|
94
|
+
instance.fabrications
|
91
95
|
end
|
92
96
|
|
93
97
|
def self.[](fabricator)
|
94
|
-
fabrications[fabricator.to_sym]
|
98
|
+
instance.fabrications[fabricator.to_sym]
|
95
99
|
end
|
96
100
|
|
97
101
|
def self.[]=(fabricator, fabrication)
|
98
|
-
fabrications[fabricator.to_sym] = fabrication
|
102
|
+
instance.fabrications[fabricator.to_sym] = fabrication
|
103
|
+
end
|
104
|
+
|
105
|
+
def fabrications
|
106
|
+
@fabrications ||= {}
|
99
107
|
end
|
100
108
|
end
|
101
109
|
end
|
@@ -6,15 +6,15 @@ module Fabrication
|
|
6
6
|
# so we can't assume because we have the ActiveRecord module that we also
|
7
7
|
# have ActiveRecord::Base. Because defined? can return nil we ensure that nil
|
8
8
|
# becomes false.
|
9
|
-
|
10
|
-
klass.ancestors.include?(::ActiveRecord::Base) || false
|
9
|
+
(defined?(::ActiveRecord::Base) &&
|
10
|
+
klass.ancestors.include?(::ActiveRecord::Base)) || false
|
11
11
|
end
|
12
12
|
|
13
13
|
def build_instance
|
14
|
-
self._instance = if
|
15
|
-
|
14
|
+
self._instance = if resolved_class.respond_to?(:protected_attributes)
|
15
|
+
resolved_class.new(_attributes, without_protection: true)
|
16
16
|
else
|
17
|
-
|
17
|
+
resolved_class.new(_attributes)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Fabrication
|
2
2
|
module Generator
|
3
3
|
class Base
|
4
|
-
def self.supports?(
|
4
|
+
def self.supports?(_resolved_class)
|
5
5
|
true
|
6
6
|
end
|
7
7
|
|
@@ -11,6 +11,9 @@ module Fabrication
|
|
11
11
|
if callbacks[:initialize_with]
|
12
12
|
build_instance_with_constructor_override(callbacks[:initialize_with])
|
13
13
|
elsif callbacks[:on_init]
|
14
|
+
Fabrication::Support.log_deprecation(
|
15
|
+
'The on_init callback has been replaced by initialize_with. Please see the documentation for usage'
|
16
|
+
)
|
14
17
|
build_instance_with_init_callback(callbacks[:on_init])
|
15
18
|
else
|
16
19
|
build_instance
|
@@ -19,18 +22,29 @@ module Fabrication
|
|
19
22
|
_instance
|
20
23
|
end
|
21
24
|
|
22
|
-
def create(attributes = [], callbacks =
|
25
|
+
def create(attributes = [], callbacks = {})
|
23
26
|
build(attributes, callbacks)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
execute_deprecated_callbacks(callbacks, :before_validation, :before_create)
|
28
|
+
execute_deprecated_callbacks(callbacks, :after_validation, :before_create)
|
29
|
+
execute_deprecated_callbacks(callbacks, :before_save, :before_create)
|
27
30
|
execute_callbacks(callbacks[:before_create])
|
28
31
|
persist
|
29
32
|
execute_callbacks(callbacks[:after_create])
|
30
|
-
|
33
|
+
execute_deprecated_callbacks(callbacks, :after_save, :after_create)
|
31
34
|
_instance
|
32
35
|
end
|
33
36
|
|
37
|
+
def execute_deprecated_callbacks(callbacks, callback_type, replacement_callback)
|
38
|
+
if callbacks[callback_type]
|
39
|
+
Fabrication::Support.log_deprecation(
|
40
|
+
"Using #{callback_type} is deprecated but you can replace it " \
|
41
|
+
"with #{replacement_callback} with the same result."
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
execute_callbacks(callbacks[callback_type])
|
46
|
+
end
|
47
|
+
|
34
48
|
def execute_callbacks(callbacks)
|
35
49
|
callbacks&.each { |callback| _instance.instance_exec(_instance, _transient_attributes, &callback) }
|
36
50
|
end
|
@@ -54,61 +68,68 @@ module Fabrication
|
|
54
68
|
end
|
55
69
|
|
56
70
|
def build_instance_with_constructor_override(callback)
|
57
|
-
self._instance =
|
71
|
+
self._instance = instance_exec(_transient_attributes, &callback)
|
58
72
|
set_attributes
|
59
73
|
end
|
60
74
|
|
61
75
|
def build_instance_with_init_callback(callback)
|
62
|
-
self._instance =
|
76
|
+
self._instance = resolved_class.new(*callback.call(_transient_attributes))
|
63
77
|
set_attributes
|
64
78
|
end
|
65
79
|
|
66
80
|
def build_instance
|
67
|
-
self._instance =
|
81
|
+
self._instance = resolved_class.new
|
68
82
|
set_attributes
|
69
83
|
end
|
70
84
|
|
71
85
|
def set_attributes
|
72
|
-
|
73
|
-
_instance.
|
74
|
-
else
|
75
|
-
_attributes.each do |k, v|
|
76
|
-
_instance.send("#{k}=", v)
|
77
|
-
end
|
86
|
+
_attributes.each do |k, v|
|
87
|
+
_instance.send(:"#{k}=", v)
|
78
88
|
end
|
79
89
|
end
|
80
90
|
|
81
|
-
def initialize(
|
82
|
-
self.
|
91
|
+
def initialize(resolved_class)
|
92
|
+
self.resolved_class = resolved_class
|
83
93
|
end
|
84
94
|
|
85
95
|
def respond_to_missing?(method_name, _include_private = false)
|
86
96
|
_attributes.key?(method_name)
|
87
97
|
end
|
88
98
|
|
89
|
-
def method_missing(method_name, *args, &
|
99
|
+
def method_missing(method_name, *args, &)
|
90
100
|
_attributes.fetch(method_name) { super }
|
91
101
|
end
|
92
102
|
|
103
|
+
def _klass
|
104
|
+
Fabrication::Support.log_deprecation(
|
105
|
+
'The `_klass` method in fabricator definitions has been replaced by `resolved_class`'
|
106
|
+
)
|
107
|
+
|
108
|
+
resolved_class
|
109
|
+
end
|
110
|
+
|
93
111
|
protected
|
94
112
|
|
95
|
-
attr_accessor :
|
113
|
+
attr_accessor :resolved_class, :_instance
|
96
114
|
|
97
115
|
def _attributes
|
98
116
|
@_attributes ||= {}
|
99
117
|
end
|
100
118
|
|
119
|
+
def _transient_attributes
|
120
|
+
@_transient_attributes ||= {}
|
121
|
+
end
|
122
|
+
|
101
123
|
def persist
|
102
124
|
_instance.save! if _instance.respond_to?(:save!)
|
103
125
|
end
|
104
126
|
|
105
127
|
def process_attributes(attributes)
|
106
|
-
self._transient_attributes = ({})
|
107
128
|
attributes.each do |attribute|
|
108
129
|
_attributes[attribute.name] = attribute.processed_value(_attributes)
|
109
130
|
_transient_attributes[attribute.name] = _attributes[attribute.name] if attribute.transient?
|
110
131
|
end
|
111
|
-
_attributes.reject! { |k| _transient_attributes.
|
132
|
+
_attributes.reject! { |k| _transient_attributes.key?(k) }
|
112
133
|
end
|
113
134
|
end
|
114
135
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Fabrication
|
2
|
+
module Generator
|
3
|
+
class Mongoid < Fabrication::Generator::Base
|
4
|
+
def self.supports?(klass)
|
5
|
+
defined?(::Mongoid) && klass.ancestors.include?(::Mongoid::Document)
|
6
|
+
end
|
7
|
+
|
8
|
+
def build_instance
|
9
|
+
self._instance = if resolved_class.respond_to?(:protected_attributes)
|
10
|
+
resolved_class.new(_attributes, without_protection: true)
|
11
|
+
else
|
12
|
+
resolved_class.new(_attributes)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -7,18 +7,15 @@ module Fabrication
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.supports?(klass)
|
10
|
-
defined?(::Sequel
|
10
|
+
defined?(::Sequel::Model) && klass.ancestors.include?(::Sequel::Model)
|
11
11
|
end
|
12
12
|
|
13
13
|
def set_attributes
|
14
|
-
_attributes.each do |
|
15
|
-
if
|
16
|
-
|
17
|
-
_instance.after_save_hook do
|
18
|
-
value.each { |o| _instance.send(reflection.add_method, o) }
|
19
|
-
end
|
14
|
+
_attributes.each do |field_name, value|
|
15
|
+
if value.is_a?(Array) && (association = association_for(field_name))
|
16
|
+
set_association(association, field_name, value)
|
20
17
|
else
|
21
|
-
|
18
|
+
set_attribute(field_name, value)
|
22
19
|
end
|
23
20
|
end
|
24
21
|
end
|
@@ -29,8 +26,23 @@ module Fabrication
|
|
29
26
|
|
30
27
|
private
|
31
28
|
|
29
|
+
def association_for(field_name)
|
30
|
+
resolved_class.association_reflections[field_name]
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_attribute(field_name, value)
|
34
|
+
_instance.send(:"#{field_name}=", value)
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_association(association, field_name, value)
|
38
|
+
_instance.associations[field_name] = value
|
39
|
+
_instance.after_save_hook do
|
40
|
+
value.each { |o| _instance.send(association.add_method, o) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
32
44
|
def load_instance_hooks
|
33
|
-
klass =
|
45
|
+
klass = resolved_class.respond_to?(:cti_base_model) ? resolved_class.cti_models.first : resolved_class
|
34
46
|
klass.plugin :instance_hooks unless klass.new.respond_to? :after_save_hook
|
35
47
|
end
|
36
48
|
end
|
@@ -43,8 +43,8 @@ module Fabrication
|
|
43
43
|
|
44
44
|
private
|
45
45
|
|
46
|
-
def execute(
|
47
|
-
Fabrication::Schematic::Runner.new(klass).instance_exec(
|
46
|
+
def execute(...)
|
47
|
+
Fabrication::Schematic::Runner.new(klass).instance_exec(...)
|
48
48
|
end
|
49
49
|
|
50
50
|
def process_count
|
@@ -65,7 +65,7 @@ module Fabrication
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def rand_range
|
68
|
-
Kernel.rand(
|
68
|
+
Kernel.rand(params[:start_range]..params[:end_range]) if params[:start_range] && params[:end_range]
|
69
69
|
end
|
70
70
|
end
|
71
71
|
end
|
@@ -4,6 +4,7 @@ module Fabrication
|
|
4
4
|
GENERATORS = [
|
5
5
|
Fabrication::Generator::ActiveRecord,
|
6
6
|
Fabrication::Generator::Sequel,
|
7
|
+
Fabrication::Generator::Mongoid,
|
7
8
|
Fabrication::Generator::Base
|
8
9
|
].freeze
|
9
10
|
|
@@ -16,15 +17,15 @@ module Fabrication
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def process_block(&block)
|
19
|
-
Fabrication::Schematic::Evaluator.new.process(self, &block) if
|
20
|
+
Fabrication::Schematic::Evaluator.new.process(self, &block) if block
|
20
21
|
end
|
21
22
|
|
22
23
|
def attribute(name)
|
23
24
|
attributes.detect { |a| a.name == name }
|
24
25
|
end
|
25
26
|
|
26
|
-
def append_or_update_attribute(attribute_name, value, params = {}, &
|
27
|
-
attribute = Fabrication::Schematic::Attribute.new(klass, attribute_name, value, params, &
|
27
|
+
def append_or_update_attribute(attribute_name, value, params = {}, &)
|
28
|
+
attribute = Fabrication::Schematic::Attribute.new(klass, attribute_name, value, params, &)
|
28
29
|
index = attributes.index { |a| a.name == attribute.name }
|
29
30
|
|
30
31
|
if index
|
@@ -55,14 +56,14 @@ module Fabrication
|
|
55
56
|
attributes.select(&:value_static?) + attributes.select(&:value_proc?)
|
56
57
|
end
|
57
58
|
|
58
|
-
def build(overrides = {}, &
|
59
|
+
def build(overrides = {}, &)
|
59
60
|
Fabrication.manager.prevent_recursion!
|
60
61
|
if Fabrication.manager.to_params_stack.any?
|
61
|
-
to_params(overrides, &
|
62
|
+
to_params(overrides, &)
|
62
63
|
else
|
63
64
|
begin
|
64
65
|
Fabrication.manager.build_stack << name
|
65
|
-
merge(overrides, &
|
66
|
+
merge(overrides, &).instance_eval do
|
66
67
|
generator.new(klass).build(sorted_attributes, callbacks)
|
67
68
|
end
|
68
69
|
ensure
|
@@ -71,16 +72,16 @@ module Fabrication
|
|
71
72
|
end
|
72
73
|
end
|
73
74
|
|
74
|
-
def fabricate(overrides = {}, &
|
75
|
+
def fabricate(overrides = {}, &)
|
75
76
|
Fabrication.manager.prevent_recursion!
|
76
77
|
if Fabrication.manager.build_stack.any?
|
77
|
-
build(overrides, &
|
78
|
+
build(overrides, &)
|
78
79
|
elsif Fabrication.manager.to_params_stack.any?
|
79
|
-
to_params(overrides, &
|
80
|
+
to_params(overrides, &)
|
80
81
|
else
|
81
82
|
begin
|
82
83
|
Fabrication.manager.create_stack << name
|
83
|
-
merge(overrides, &
|
84
|
+
merge(overrides, &).instance_eval do
|
84
85
|
generator.new(klass).create(sorted_attributes, callbacks)
|
85
86
|
end
|
86
87
|
ensure
|
@@ -89,18 +90,18 @@ module Fabrication
|
|
89
90
|
end
|
90
91
|
end
|
91
92
|
|
92
|
-
def to_params(overrides = {}, &
|
93
|
+
def to_params(overrides = {}, &)
|
93
94
|
Fabrication.manager.prevent_recursion!
|
94
95
|
Fabrication.manager.to_params_stack << name
|
95
|
-
merge(overrides, &
|
96
|
+
merge(overrides, &).instance_eval do
|
96
97
|
generator.new(klass).to_params(sorted_attributes)
|
97
98
|
end
|
98
99
|
ensure
|
99
100
|
Fabrication.manager.to_params_stack.pop
|
100
101
|
end
|
101
102
|
|
102
|
-
def to_attributes(overrides = {}, &
|
103
|
-
merge(overrides, &
|
103
|
+
def to_attributes(overrides = {}, &)
|
104
|
+
merge(overrides, &).instance_eval do
|
104
105
|
generator.new(klass).to_hash(sorted_attributes, callbacks)
|
105
106
|
end
|
106
107
|
end
|
@@ -123,9 +124,9 @@ module Fabrication
|
|
123
124
|
end
|
124
125
|
end
|
125
126
|
|
126
|
-
def merge(overrides = {}, &
|
127
|
+
def merge(overrides = {}, &)
|
127
128
|
clone.tap do |definition|
|
128
|
-
definition.process_block(&
|
129
|
+
definition.process_block(&)
|
129
130
|
overrides.each do |name, value|
|
130
131
|
definition.append_or_update_attribute(name.to_sym, value)
|
131
132
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module Fabrication
|
2
2
|
module Schematic
|
3
3
|
class Evaluator < BasicObject
|
4
|
-
def process(definition, &
|
4
|
+
def process(definition, &)
|
5
5
|
@_definition = definition
|
6
|
-
instance_eval(&
|
6
|
+
instance_eval(&)
|
7
7
|
end
|
8
8
|
|
9
9
|
def respond_to_missing?(_method_name, _include_private = false)
|
@@ -30,10 +30,10 @@ module Fabrication
|
|
30
30
|
@initializing = false
|
31
31
|
end
|
32
32
|
|
33
|
-
def register(name, options, &
|
33
|
+
def register(name, options, &)
|
34
34
|
name = name.to_sym
|
35
35
|
raise_if_registered(name)
|
36
|
-
store(name, Array(options.delete(:aliases)), options, &
|
36
|
+
store(name, Array(options.delete(:aliases)), options, &)
|
37
37
|
end
|
38
38
|
|
39
39
|
def [](name)
|
@@ -55,14 +55,16 @@ module Fabrication
|
|
55
55
|
def load_definitions
|
56
56
|
preinitialize
|
57
57
|
Fabrication::Config.path_prefixes.each do |prefix|
|
58
|
-
Fabrication::Config.fabricator_paths.each do |
|
59
|
-
|
60
|
-
load
|
58
|
+
Fabrication::Config.fabricator_paths.each do |path|
|
59
|
+
if File.file?(path)
|
60
|
+
load path
|
61
|
+
else
|
62
|
+
Dir.glob(File.join(prefix.to_s, path, '**', '*.rb')).each do |file|
|
63
|
+
load file
|
64
|
+
end
|
61
65
|
end
|
62
66
|
end
|
63
67
|
end
|
64
|
-
rescue StandardError => e
|
65
|
-
raise e
|
66
68
|
ensure
|
67
69
|
freeze
|
68
70
|
end
|
@@ -79,8 +81,8 @@ module Fabrication
|
|
79
81
|
(raise Fabrication::DuplicateFabricatorError, name) if self[name]
|
80
82
|
end
|
81
83
|
|
82
|
-
def store(name, aliases, options, &
|
83
|
-
schematic = schematics[name] = Fabrication::Schematic::Definition.new(name, options, &
|
84
|
+
def store(name, aliases, options, &)
|
85
|
+
schematic = schematics[name] = Fabrication::Schematic::Definition.new(name, options, &)
|
84
86
|
aliases.each { |as| schematics[as.to_sym] = schematic }
|
85
87
|
end
|
86
88
|
end
|
@@ -7,9 +7,9 @@ module Fabrication
|
|
7
7
|
self.klass = klass
|
8
8
|
end
|
9
9
|
|
10
|
-
def sequence(name = Fabrication::Sequencer::DEFAULT, start = nil, &
|
11
|
-
name = "#{klass.to_s.downcase.gsub(
|
12
|
-
Fabrication::Sequencer.sequence(name, start, &
|
10
|
+
def sequence(name = Fabrication::Sequencer::DEFAULT, start = nil, &)
|
11
|
+
name = "#{klass.to_s.downcase.gsub('::', '_')}_#{name}"
|
12
|
+
Fabrication::Sequencer.sequence(name, start, &)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -1,27 +1,40 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
1
3
|
module Fabrication
|
2
4
|
class Sequencer
|
5
|
+
include Singleton
|
6
|
+
|
3
7
|
DEFAULT = :_default
|
4
8
|
|
5
|
-
def self.sequence(name = DEFAULT, start = nil, &
|
9
|
+
def self.sequence(name = DEFAULT, start = nil, &)
|
10
|
+
instance.sequence(name, start, &)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.clear
|
14
|
+
instance.sequences.clear
|
15
|
+
instance.sequence_blocks.clear
|
16
|
+
end
|
17
|
+
|
18
|
+
def sequence(name = DEFAULT, start = nil, &block)
|
6
19
|
idx = sequences[name] ||= start || Fabrication::Config.sequence_start
|
7
|
-
if
|
20
|
+
if block
|
8
21
|
sequence_blocks[name] = block.to_proc
|
9
22
|
else
|
10
23
|
sequence_blocks[name] ||= ->(i) { i }
|
11
24
|
end.call(idx).tap do
|
12
|
-
sequences[name]
|
25
|
+
sequences[name] = idx.succ
|
13
26
|
end
|
14
27
|
end
|
15
28
|
|
16
|
-
def
|
29
|
+
def sequences
|
17
30
|
@sequences ||= {}
|
18
31
|
end
|
19
32
|
|
20
|
-
def
|
33
|
+
def sequence_blocks
|
21
34
|
@sequence_blocks ||= {}
|
22
35
|
end
|
23
36
|
|
24
|
-
def
|
37
|
+
def reset
|
25
38
|
Fabrication::Config.sequence_start = nil
|
26
39
|
@sequences = nil
|
27
40
|
@sequence_blocks = nil
|
data/lib/fabrication/support.rb
CHANGED
@@ -1,73 +1,68 @@
|
|
1
1
|
module Fabrication
|
2
|
-
|
3
|
-
|
4
|
-
def fabricatable?(name)
|
5
|
-
Fabrication.manager[name] || class_for(name)
|
6
|
-
end
|
2
|
+
module Support
|
3
|
+
extend self
|
7
4
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
rescue NameError => e
|
12
|
-
raise Fabrication::UnfabricatableError.new(class_or_to_s, e)
|
13
|
-
end
|
5
|
+
def fabricatable?(name)
|
6
|
+
Fabrication.manager[name] || class_for(name)
|
7
|
+
end
|
14
8
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
names.shift if names.size > 1 && names.first.empty?
|
19
|
-
names.inject(Object) do |constant, name|
|
20
|
-
if constant == Object
|
21
|
-
constant.const_get(name)
|
22
|
-
else
|
23
|
-
candidate = constant.const_get(name)
|
24
|
-
next candidate if constant.const_defined?(name, false)
|
25
|
-
next candidate unless Object.const_defined?(name)
|
9
|
+
def log_deprecation(message)
|
10
|
+
Config.logger.warn("[DEPRECATION][fabrication] #{message}")
|
11
|
+
end
|
26
12
|
|
27
|
-
|
28
|
-
|
29
|
-
|
13
|
+
def class_for(class_or_to_s)
|
14
|
+
constantize(variable_name_to_class_name(class_or_to_s))
|
15
|
+
rescue NameError => e
|
16
|
+
raise Fabrication::UnfabricatableError.new(class_or_to_s, e)
|
17
|
+
end
|
30
18
|
|
31
|
-
|
32
|
-
|
33
|
-
constant.const_get(name, false)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
19
|
+
def constantize(camel_cased_word)
|
20
|
+
return camel_cased_word if camel_cased_word.is_a?(Class)
|
37
21
|
|
38
|
-
|
39
|
-
|
22
|
+
camel_cased_word.to_s.split('::').reduce(Object) do |resolved_class, class_part|
|
23
|
+
resolved_class.const_get(class_part)
|
40
24
|
end
|
25
|
+
end
|
41
26
|
|
42
|
-
|
43
|
-
|
27
|
+
def extract_options!(args)
|
28
|
+
args.last.is_a?(::Hash) ? args.pop : {}
|
29
|
+
end
|
44
30
|
|
45
|
-
|
46
|
-
|
47
|
-
else
|
48
|
-
name_string.gsub(%r{/(.?)}) do
|
49
|
-
"::#{Regexp.last_match(1).upcase}"
|
50
|
-
end.gsub(/(?:^|_)(.)/) { Regexp.last_match(1).upcase }
|
51
|
-
end
|
52
|
-
end
|
31
|
+
def variable_name_to_class_name(name)
|
32
|
+
name_string = name.to_s
|
53
33
|
|
54
|
-
|
55
|
-
|
34
|
+
if name_string.respond_to?(:camelize)
|
35
|
+
name_string.camelize
|
36
|
+
else
|
37
|
+
name_string
|
38
|
+
.gsub(%r{/(.?)}) { "::#{Regexp.last_match(1).upcase}" }
|
39
|
+
.gsub(/(?:^|_)(.)/) { Regexp.last_match(1).upcase }
|
56
40
|
end
|
41
|
+
end
|
57
42
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
string.end_with?('s') ? string[0..-2] : string
|
62
|
-
end
|
43
|
+
def find_definitions
|
44
|
+
log_deprecation('Fabrication::Support.find_definitions has been replaced by ' \
|
45
|
+
'Fabrication.manager.load_definitions and will be removed in 3.0.0.')
|
63
46
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
47
|
+
Fabrication.manager.load_definitions
|
48
|
+
end
|
49
|
+
|
50
|
+
def hash_class
|
51
|
+
@hash_class ||= defined?(HashWithIndifferentAccess) ? HashWithIndifferentAccess : Hash
|
52
|
+
end
|
53
|
+
|
54
|
+
def singularize(string)
|
55
|
+
string.singularize
|
56
|
+
rescue StandardError
|
57
|
+
string.end_with?('s') ? string[0..-2] : string
|
58
|
+
end
|
59
|
+
|
60
|
+
def underscore(string)
|
61
|
+
string.gsub('::', '/')
|
62
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
63
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
64
|
+
.tr('-', '_')
|
65
|
+
.downcase
|
71
66
|
end
|
72
67
|
end
|
73
68
|
end
|
@@ -11,18 +11,18 @@ module Fabrication
|
|
11
11
|
#
|
12
12
|
#
|
13
13
|
module Make
|
14
|
-
def make(*args, &
|
14
|
+
def make(*args, &)
|
15
15
|
overrides = Fabrication::Support.extract_options!(args)
|
16
16
|
klass = Fabrication::Support.underscore(name).to_sym
|
17
17
|
fabricator_name = args.first.is_a?(Symbol) ? "#{klass}_#{args.first}" : klass
|
18
|
-
Fabricate.build(fabricator_name, overrides, &
|
18
|
+
Fabricate.build(fabricator_name, overrides, &)
|
19
19
|
end
|
20
20
|
|
21
|
-
def make!(*args, &
|
21
|
+
def make!(*args, &)
|
22
22
|
overrides = Fabrication::Support.extract_options!(args)
|
23
23
|
klass = Fabrication::Support.underscore(name).to_sym
|
24
24
|
fabricator_name = args.first.is_a?(Symbol) ? "#{klass}_#{args.first}" : klass
|
25
|
-
Fabricate(fabricator_name, overrides, &
|
25
|
+
Fabricate(fabricator_name, overrides, &)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -1,37 +1,39 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
1
3
|
module Fabrication
|
2
4
|
class Transform
|
5
|
+
include Singleton
|
6
|
+
|
3
7
|
class << self
|
4
8
|
def apply_to(schematic, attributes_hash)
|
5
9
|
Fabrication.manager.load_definitions if Fabrication.manager.empty?
|
6
|
-
attributes_hash.inject({}) { |h, (k, v)| h.update(k => apply_transform(schematic, k, v)) }
|
10
|
+
attributes_hash.inject({}) { |h, (k, v)| h.update(k => instance.apply_transform(schematic, k, v)) }
|
7
11
|
end
|
8
12
|
|
9
13
|
def clear_all
|
10
|
-
|
11
|
-
|
14
|
+
instance.transforms.clear
|
15
|
+
instance.overrides.clear
|
12
16
|
end
|
13
17
|
|
14
18
|
def define(attribute, transform)
|
15
|
-
transforms[attribute] = transform
|
19
|
+
instance.transforms[attribute] = transform
|
16
20
|
end
|
17
21
|
|
18
22
|
def only_for(schematic, attribute, transform)
|
19
|
-
overrides[schematic] = (overrides[schematic] || {}).merge!(attribute => transform)
|
23
|
+
instance.overrides[schematic] = (instance.overrides[schematic] || {}).merge!(attribute => transform)
|
20
24
|
end
|
25
|
+
end
|
21
26
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@overrides ||= {}
|
26
|
-
end
|
27
|
+
def overrides
|
28
|
+
@overrides ||= {}
|
29
|
+
end
|
27
30
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
+
def apply_transform(schematic, attribute, value)
|
32
|
+
overrides.fetch(schematic, transforms)[attribute].call(value)
|
33
|
+
end
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
+
def transforms
|
36
|
+
@transforms ||= Hash.new(->(value) { value })
|
35
37
|
end
|
36
38
|
end
|
37
39
|
end
|
data/lib/fabrication/version.rb
CHANGED
data/lib/fabrication.rb
CHANGED
@@ -31,34 +31,38 @@ module Fabrication
|
|
31
31
|
|
32
32
|
module Generator
|
33
33
|
autoload :ActiveRecord, 'fabrication/generator/active_record'
|
34
|
-
autoload :
|
34
|
+
autoload :Mongoid, 'fabrication/generator/mongoid'
|
35
35
|
autoload :Sequel, 'fabrication/generator/sequel'
|
36
36
|
autoload :Base, 'fabrication/generator/base'
|
37
37
|
end
|
38
38
|
|
39
39
|
def self.clear_definitions
|
40
40
|
manager.clear
|
41
|
-
Sequencer.
|
41
|
+
Sequencer.clear
|
42
42
|
end
|
43
43
|
|
44
|
-
def self.configure(&
|
45
|
-
Fabrication::Config.configure(&
|
44
|
+
def self.configure(&)
|
45
|
+
Fabrication::Config.configure(&)
|
46
46
|
end
|
47
47
|
|
48
48
|
def self.manager
|
49
|
-
|
49
|
+
Fabrication::Schematic::Manager.instance
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.schematics
|
53
|
+
Support.log_deprecation('Fabrication.schematics has been replaced by ' \
|
54
|
+
'Fabrication.manager and will be removed in 3.0.0.')
|
55
|
+
manager
|
50
56
|
end
|
51
57
|
end
|
52
58
|
|
53
59
|
# rubocop:disable Naming/MethodName
|
54
|
-
def Fabricator(name, options = {}, &
|
55
|
-
Fabrication.manager.register(name, options, &
|
60
|
+
def Fabricator(name, options = {}, &)
|
61
|
+
Fabrication.manager.register(name, options, &)
|
56
62
|
end
|
57
63
|
|
58
|
-
def Fabricate(name, overrides = {}, &
|
59
|
-
Fabricate.create(name, overrides, &
|
60
|
-
Fabrication::Cucumber::Fabrications[name] = object if Fabrication::Config.register_with_steps?
|
61
|
-
end
|
64
|
+
def Fabricate(name, overrides = {}, &)
|
65
|
+
Fabricate.create(name, overrides, &)
|
62
66
|
end
|
63
67
|
# rubocop:enable Naming/MethodName
|
64
68
|
|
@@ -1,12 +1,14 @@
|
|
1
1
|
World(FabricationMethods)
|
2
2
|
|
3
|
-
Fabrication::Config.
|
3
|
+
Fabrication::Config.register_notifier do |name, object|
|
4
|
+
Fabrication::Cucumber::Fabrications[name] = object
|
5
|
+
end
|
4
6
|
|
5
7
|
def with_ivars(fabricator)
|
6
8
|
@they = yield fabricator
|
7
9
|
model = @they.last.class.to_s.underscore
|
8
|
-
instance_variable_set("@#{model.pluralize}", @they)
|
9
|
-
instance_variable_set("@#{model.singularize}", @they.last)
|
10
|
+
instance_variable_set(:"@#{model.pluralize}", @they)
|
11
|
+
instance_variable_set(:"@#{model.singularize}", @they.last)
|
10
12
|
Fabrication::Cucumber::Fabrications[model.singularize.gsub(/\W+/, '_').downcase] = @they.last
|
11
13
|
end
|
12
14
|
|
@@ -23,13 +25,13 @@ Given(/^the following ([^"]*):$/) do |model_name, table|
|
|
23
25
|
end
|
24
26
|
|
25
27
|
Given(/^that ([^"]*) has the following ([^"]*):$/) do |parent, child, table|
|
26
|
-
with_ivars Fabrication::Cucumber::StepFabricator.new(child, parent:
|
28
|
+
with_ivars Fabrication::Cucumber::StepFabricator.new(child, parent:) do |fab|
|
27
29
|
fab.from_table(table)
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
33
|
Given(/^that ([^"]*) has (\d+) ([^"]*)$/) do |parent, count, child|
|
32
|
-
with_ivars Fabrication::Cucumber::StepFabricator.new(child, parent:
|
34
|
+
with_ivars Fabrication::Cucumber::StepFabricator.new(child, parent:) do |fab|
|
33
35
|
fab.n(count.to_i)
|
34
36
|
end
|
35
37
|
end
|
@@ -19,7 +19,7 @@ module Fabrication
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def self.source_root
|
22
|
-
|
22
|
+
File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
|
23
23
|
end
|
24
24
|
|
25
25
|
private
|
@@ -33,6 +33,8 @@ module Fabrication
|
|
33
33
|
end
|
34
34
|
rescue StandardError
|
35
35
|
# no table? no problem!
|
36
|
+
rescue LoadError
|
37
|
+
# cannot find model file. This means it was already destroyed, so just continue.
|
36
38
|
end
|
37
39
|
end
|
38
40
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fabrication
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Elliott
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Fabrication is an object generation framework for ActiveRecord, Mongoid,
|
14
14
|
Sequel, or any other Ruby object.
|
@@ -32,6 +32,7 @@ files:
|
|
32
32
|
- lib/fabrication/errors/unknown_fabricator_error.rb
|
33
33
|
- lib/fabrication/generator/active_record.rb
|
34
34
|
- lib/fabrication/generator/base.rb
|
35
|
+
- lib/fabrication/generator/mongoid.rb
|
35
36
|
- lib/fabrication/generator/sequel.rb
|
36
37
|
- lib/fabrication/railtie.rb
|
37
38
|
- lib/fabrication/schematic/attribute.rb
|
@@ -52,8 +53,15 @@ files:
|
|
52
53
|
homepage: http://fabricationgem.org
|
53
54
|
licenses:
|
54
55
|
- MIT
|
55
|
-
metadata:
|
56
|
-
|
56
|
+
metadata:
|
57
|
+
bug_tracker_uri: https://gitlab.com/fabrication-gem/fabrication/-/issues
|
58
|
+
changelog_uri: https://gitlab.com/fabrication-gem/fabrication/-/blob/master/Changelog.markdown
|
59
|
+
documentation_uri: https://fabricationgem.org
|
60
|
+
homepage_uri: https://fabricationgem.org
|
61
|
+
mailing_list_uri: https://groups.google.com/g/fabricationgem
|
62
|
+
rubygems_mfa_required: 'true'
|
63
|
+
source_code_uri: https://gitlab.com/fabrication-gem/fabrication
|
64
|
+
post_install_message:
|
57
65
|
rdoc_options: []
|
58
66
|
require_paths:
|
59
67
|
- lib
|
@@ -61,15 +69,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
61
69
|
requirements:
|
62
70
|
- - ">="
|
63
71
|
- !ruby/object:Gem::Version
|
64
|
-
version: 2.
|
72
|
+
version: 3.2.0
|
65
73
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
74
|
requirements:
|
67
|
-
- - "
|
75
|
+
- - ">="
|
68
76
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
77
|
+
version: '0'
|
70
78
|
requirements: []
|
71
|
-
rubygems_version: 3.
|
72
|
-
signing_key:
|
79
|
+
rubygems_version: 3.4.19
|
80
|
+
signing_key:
|
73
81
|
specification_version: 4
|
74
|
-
summary:
|
82
|
+
summary: Generates object instances for test suites, seed files, etc.
|
75
83
|
test_files: []
|