formed 1.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 +7 -0
- data/README.md +146 -0
- data/Rakefile +12 -0
- data/lib/active_form.rb +12 -0
- data/lib/formed/acts_like_model.rb +27 -0
- data/lib/formed/association_relation.rb +22 -0
- data/lib/formed/associations/association.rb +193 -0
- data/lib/formed/associations/builder/association.rb +116 -0
- data/lib/formed/associations/builder/collection_association.rb +71 -0
- data/lib/formed/associations/builder/has_many.rb +24 -0
- data/lib/formed/associations/builder/has_one.rb +44 -0
- data/lib/formed/associations/builder/singular_association.rb +46 -0
- data/lib/formed/associations/builder.rb +13 -0
- data/lib/formed/associations/collection_association.rb +296 -0
- data/lib/formed/associations/collection_proxy.rb +519 -0
- data/lib/formed/associations/foreign_association.rb +37 -0
- data/lib/formed/associations/has_many_association.rb +63 -0
- data/lib/formed/associations/has_one_association.rb +27 -0
- data/lib/formed/associations/singular_association.rb +66 -0
- data/lib/formed/associations.rb +62 -0
- data/lib/formed/attributes.rb +42 -0
- data/lib/formed/base.rb +183 -0
- data/lib/formed/core.rb +73 -0
- data/lib/formed/from_model.rb +41 -0
- data/lib/formed/from_params.rb +33 -0
- data/lib/formed/inheritance.rb +179 -0
- data/lib/formed/nested_attributes.rb +287 -0
- data/lib/formed/reflection.rb +781 -0
- data/lib/formed/relation/delegation.rb +147 -0
- data/lib/formed/relation.rb +113 -0
- data/lib/formed/version.rb +3 -0
- data/lib/generators/active_form/form_generator.rb +72 -0
- data/lib/generators/active_form/templates/form.rb.tt +8 -0
- data/lib/generators/active_form/templates/form_spec.rb.tt +5 -0
- data/lib/generators/active_form/templates/module.rb.tt +4 -0
- metadata +203 -0
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formed
|
4
|
+
class Relation
|
5
|
+
module Delegation # :nodoc:
|
6
|
+
class << self
|
7
|
+
def delegated_classes
|
8
|
+
[
|
9
|
+
Formed::Relation,
|
10
|
+
Formed::Associations::CollectionProxy,
|
11
|
+
Formed::AssociationRelation
|
12
|
+
]
|
13
|
+
end
|
14
|
+
|
15
|
+
def uncacheable_methods
|
16
|
+
@uncacheable_methods ||= (
|
17
|
+
delegated_classes.flat_map(&:public_instance_methods) - Formed::Relation.public_instance_methods
|
18
|
+
).to_set.freeze
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module DelegateCache # :nodoc:
|
23
|
+
def relation_delegate_class(klass)
|
24
|
+
@relation_delegate_cache[klass]
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize_relation_delegate_cache
|
28
|
+
@relation_delegate_cache = cache = {}
|
29
|
+
Delegation.delegated_classes.each do |klass|
|
30
|
+
delegate = Class.new(klass) do
|
31
|
+
include ClassSpecificRelation
|
32
|
+
end
|
33
|
+
include_relation_methods(delegate)
|
34
|
+
mangled_name = klass.name.gsub("::", "_")
|
35
|
+
const_set mangled_name, delegate
|
36
|
+
private_constant mangled_name
|
37
|
+
|
38
|
+
cache[klass] = delegate
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def inherited(child_class)
|
43
|
+
child_class.initialize_relation_delegate_cache
|
44
|
+
super
|
45
|
+
end
|
46
|
+
|
47
|
+
def generate_relation_method(method)
|
48
|
+
generated_relation_methods.generate_method(method)
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def include_relation_methods(delegate)
|
54
|
+
superclass.include_relation_methods(delegate) unless base_class?
|
55
|
+
delegate.include generated_relation_methods
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def generated_relation_methods
|
61
|
+
@generated_relation_methods ||= GeneratedRelationMethods.new.tap do |mod|
|
62
|
+
const_set(:GeneratedRelationMethods, mod)
|
63
|
+
private_constant :GeneratedRelationMethods
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class GeneratedRelationMethods < Module # :nodoc:
|
69
|
+
include Mutex_m
|
70
|
+
|
71
|
+
def generate_method(method)
|
72
|
+
synchronize do
|
73
|
+
return if method_defined?(method)
|
74
|
+
|
75
|
+
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method) && !DELEGATION_RESERVED_METHOD_NAMES.include?(method.to_s)
|
76
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
77
|
+
def #{method}(...)
|
78
|
+
scoping { klass.#{method}(...) }
|
79
|
+
end
|
80
|
+
RUBY
|
81
|
+
else
|
82
|
+
define_method(method) do |*args, &block|
|
83
|
+
scoping { klass.public_send(method, *args, &block) }
|
84
|
+
end
|
85
|
+
ruby2_keywords(method)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
private_constant :GeneratedRelationMethods
|
91
|
+
|
92
|
+
extend ActiveSupport::Concern
|
93
|
+
|
94
|
+
# This module creates compiled delegation methods dynamically at runtime, which makes
|
95
|
+
# subsequent calls to that method faster by avoiding method_missing. The delegations
|
96
|
+
# may vary depending on the klass of a relation, so we create a subclass of Relation
|
97
|
+
# for each different klass, and the delegations are compiled into that subclass only.
|
98
|
+
|
99
|
+
delegate :to_xml, :encode_with, :length, :each, :join, :intersects?,
|
100
|
+
:[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
|
101
|
+
:to_sentence, :to_fs, :to_formatted_s, :as_json,
|
102
|
+
:shuffle, :split, :slice, :index, :rindex, to: :records
|
103
|
+
|
104
|
+
delegate :primary_key, :connection, to: :klass
|
105
|
+
|
106
|
+
module ClassSpecificRelation # :nodoc:
|
107
|
+
extend ActiveSupport::Concern
|
108
|
+
|
109
|
+
module ClassMethods # :nodoc:
|
110
|
+
def name
|
111
|
+
superclass.name
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def method_missing(method, *args, &block)
|
118
|
+
if @klass.respond_to?(method)
|
119
|
+
@klass.generate_relation_method(method) unless Delegation.uncacheable_methods.include?(method)
|
120
|
+
scoping { @klass.public_send(method, *args, &block) }
|
121
|
+
else
|
122
|
+
super
|
123
|
+
end
|
124
|
+
end
|
125
|
+
ruby2_keywords(:method_missing)
|
126
|
+
end
|
127
|
+
|
128
|
+
module ClassMethods # :nodoc:
|
129
|
+
def create(klass, *args, **kwargs)
|
130
|
+
relation_class_for(klass).new(klass, *args, **kwargs)
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def relation_class_for(klass)
|
136
|
+
klass.relation_delegate_class(self)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def respond_to_missing?(method, _)
|
143
|
+
super || @klass.respond_to?(method)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formed
|
4
|
+
class Relation
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
attr_reader :klass, :loaded
|
8
|
+
attr_accessor :skip_preloading_value
|
9
|
+
alias model klass
|
10
|
+
alias loaded? loaded
|
11
|
+
|
12
|
+
include Delegation
|
13
|
+
|
14
|
+
def initialize(klass, values: {})
|
15
|
+
@klass = klass
|
16
|
+
@values = values
|
17
|
+
@loaded = true
|
18
|
+
@delegate_to_klass = false
|
19
|
+
@future_result = nil
|
20
|
+
@records = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize_copy(_other)
|
24
|
+
@values = @values.dup
|
25
|
+
reset
|
26
|
+
end
|
27
|
+
|
28
|
+
# Initializes new record from relation while maintaining the current
|
29
|
+
# scope.
|
30
|
+
#
|
31
|
+
# Expects arguments in the same format as {ActiveRecord::Base.new}[rdoc-ref:Core.new].
|
32
|
+
#
|
33
|
+
# users = User.where(name: 'DHH')
|
34
|
+
# user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
|
35
|
+
#
|
36
|
+
# You can also pass a block to new with the new record as argument:
|
37
|
+
#
|
38
|
+
# user = users.new { |user| user.name = 'Oscar' }
|
39
|
+
# user.name # => Oscar
|
40
|
+
def new(attributes = nil, &block)
|
41
|
+
if attributes.is_a?(Array)
|
42
|
+
attributes.collect { |attr| new(attr, &block) }
|
43
|
+
else
|
44
|
+
block = current_scope_restoring_block(&block)
|
45
|
+
scoping { _new(attributes, &block) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
alias build new
|
49
|
+
|
50
|
+
# Converts relation objects to Array.
|
51
|
+
def to_ary
|
52
|
+
records.dup
|
53
|
+
end
|
54
|
+
alias to_a to_ary
|
55
|
+
|
56
|
+
def records # :nodoc:
|
57
|
+
@records
|
58
|
+
end
|
59
|
+
|
60
|
+
# Serializes the relation objects Array.
|
61
|
+
def encode_with(coder)
|
62
|
+
coder.represent_seq(nil, records)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns size of the records.
|
66
|
+
def size
|
67
|
+
if loaded?
|
68
|
+
records.length
|
69
|
+
else
|
70
|
+
count(:all)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns true if there are no records.
|
75
|
+
def empty?
|
76
|
+
if loaded?
|
77
|
+
records.empty?
|
78
|
+
else
|
79
|
+
!exists?
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns true if there are no records.
|
84
|
+
def none?
|
85
|
+
return super if block_given?
|
86
|
+
|
87
|
+
empty?
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns true if there are any records.
|
91
|
+
def any?
|
92
|
+
return super if block_given?
|
93
|
+
|
94
|
+
!empty?
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns true if there is exactly one record.
|
98
|
+
def one?
|
99
|
+
return super if block_given?
|
100
|
+
return records.one? if loaded?
|
101
|
+
|
102
|
+
limited_count == 1
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns true if there is more than one record.
|
106
|
+
def many?
|
107
|
+
return super if block_given?
|
108
|
+
return records.many? if loaded?
|
109
|
+
|
110
|
+
limited_count > 1
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module Formed
|
4
|
+
module Generators # :nodoc:
|
5
|
+
class FormGenerator < Rails::Generators::NamedBase # :nodoc:
|
6
|
+
source_root File.join(__dir__, "templates")
|
7
|
+
|
8
|
+
argument :attributes, type: :array, default: [], banner: "field[:type] field[:type]"
|
9
|
+
class_option :model, type: :string, desc: "The model name for the generated form"
|
10
|
+
class_option :parent, type: :string, default: "ApplicationForm", desc: "The parent class for the generated form"
|
11
|
+
|
12
|
+
def create_model_file
|
13
|
+
generate_abstract_class if !custom_parent?
|
14
|
+
|
15
|
+
template "form.rb", File.join("app/forms", class_path, "#{file_name}.rb")
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_module_file
|
19
|
+
return if regular_class_path.empty?
|
20
|
+
|
21
|
+
template "module.rb", File.join("app/forms", "#{class_path.join('/')}.rb") if behavior == :invoke
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_rspec_file
|
25
|
+
return unless Rails.application.config.generators.test_framework == :rspec
|
26
|
+
|
27
|
+
template 'form_spec.rb', File.join("spec/forms", class_path, "#{file_name}_spec.rb")
|
28
|
+
end
|
29
|
+
|
30
|
+
def file_name
|
31
|
+
name = super
|
32
|
+
unless name.end_with?("_form")
|
33
|
+
name += "_form"
|
34
|
+
end
|
35
|
+
name
|
36
|
+
end
|
37
|
+
|
38
|
+
def class_name
|
39
|
+
name = super
|
40
|
+
unless name.end_with?("Form")
|
41
|
+
name += "Form"
|
42
|
+
end
|
43
|
+
name
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def parent_class_name
|
49
|
+
parent
|
50
|
+
end
|
51
|
+
|
52
|
+
def generate_abstract_class
|
53
|
+
path = File.join("app/forms", "application_form.rb")
|
54
|
+
return if File.exist?(path)
|
55
|
+
|
56
|
+
template "abstract_base_class.rb", path
|
57
|
+
end
|
58
|
+
|
59
|
+
def abstract_class_name
|
60
|
+
"ApplicationForm"
|
61
|
+
end
|
62
|
+
|
63
|
+
def parent
|
64
|
+
options[:parent]
|
65
|
+
end
|
66
|
+
|
67
|
+
def custom_parent?
|
68
|
+
parent != self.class.class_options[:parent].default
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
metadata
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: formed
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Josh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-10-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: codecov
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: factory_bot_rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-rails
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: sqlite3
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: timecop
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: A form object that really wants to be a form object.
|
140
|
+
email:
|
141
|
+
- josh@josh.mn
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- README.md
|
147
|
+
- Rakefile
|
148
|
+
- lib/active_form.rb
|
149
|
+
- lib/formed/acts_like_model.rb
|
150
|
+
- lib/formed/association_relation.rb
|
151
|
+
- lib/formed/associations.rb
|
152
|
+
- lib/formed/associations/association.rb
|
153
|
+
- lib/formed/associations/builder.rb
|
154
|
+
- lib/formed/associations/builder/association.rb
|
155
|
+
- lib/formed/associations/builder/collection_association.rb
|
156
|
+
- lib/formed/associations/builder/has_many.rb
|
157
|
+
- lib/formed/associations/builder/has_one.rb
|
158
|
+
- lib/formed/associations/builder/singular_association.rb
|
159
|
+
- lib/formed/associations/collection_association.rb
|
160
|
+
- lib/formed/associations/collection_proxy.rb
|
161
|
+
- lib/formed/associations/foreign_association.rb
|
162
|
+
- lib/formed/associations/has_many_association.rb
|
163
|
+
- lib/formed/associations/has_one_association.rb
|
164
|
+
- lib/formed/associations/singular_association.rb
|
165
|
+
- lib/formed/attributes.rb
|
166
|
+
- lib/formed/base.rb
|
167
|
+
- lib/formed/core.rb
|
168
|
+
- lib/formed/from_model.rb
|
169
|
+
- lib/formed/from_params.rb
|
170
|
+
- lib/formed/inheritance.rb
|
171
|
+
- lib/formed/nested_attributes.rb
|
172
|
+
- lib/formed/reflection.rb
|
173
|
+
- lib/formed/relation.rb
|
174
|
+
- lib/formed/relation/delegation.rb
|
175
|
+
- lib/formed/version.rb
|
176
|
+
- lib/generators/active_form/form_generator.rb
|
177
|
+
- lib/generators/active_form/templates/form.rb.tt
|
178
|
+
- lib/generators/active_form/templates/form_spec.rb.tt
|
179
|
+
- lib/generators/active_form/templates/module.rb.tt
|
180
|
+
homepage: https://github.com/joshmn/formed
|
181
|
+
licenses:
|
182
|
+
- MIT
|
183
|
+
metadata: {}
|
184
|
+
post_install_message:
|
185
|
+
rdoc_options: []
|
186
|
+
require_paths:
|
187
|
+
- lib
|
188
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
189
|
+
requirements:
|
190
|
+
- - ">="
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: '0'
|
193
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
194
|
+
requirements:
|
195
|
+
- - ">="
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
requirements: []
|
199
|
+
rubygems_version: 3.3.11
|
200
|
+
signing_key:
|
201
|
+
specification_version: 4
|
202
|
+
summary: A form object that really wants to be a form object.
|
203
|
+
test_files: []
|