super 0.0.14 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/app/assets/javascripts/super/application.js +148 -15
  4. data/app/assets/stylesheets/super/application.css +15 -22
  5. data/app/controllers/super/application_controller.rb +44 -42
  6. data/app/controllers/super/substructure_controller.rb +313 -0
  7. data/app/helpers/super/form_builder_helper.rb +7 -0
  8. data/app/views/layouts/super/application.html.erb +3 -21
  9. data/app/views/super/application/_collection_header.html.erb +2 -2
  10. data/app/views/super/application/_display_actions.html.erb +1 -1
  11. data/app/views/super/application/_display_index.html.erb +2 -2
  12. data/app/views/super/application/_display_show.html.erb +1 -1
  13. data/app/views/super/application/_filter.html.erb +62 -2
  14. data/app/views/super/application/_form_field.html.erb +5 -0
  15. data/app/views/super/application/_member_header.html.erb +2 -2
  16. data/app/views/super/application/_pagination.html.erb +1 -1
  17. data/app/views/super/application/_site_footer.html.erb +3 -0
  18. data/app/views/super/application/_site_header.html.erb +17 -0
  19. data/app/views/super/application/_sort_expression.html.erb +2 -2
  20. data/app/views/super/application/index.csv.erb +14 -0
  21. data/app/views/super/feather/README.md +0 -1
  22. data/frontend/super-frontend/dist/application.css +15 -22
  23. data/frontend/super-frontend/dist/application.js +148 -15
  24. data/lib/generators/super/install/install_generator.rb +0 -16
  25. data/lib/generators/super/install/templates/base_controller.rb.tt +0 -8
  26. data/lib/generators/super/resource/templates/resources_controller.rb.tt +4 -4
  27. data/lib/super/action_inquirer.rb +18 -3
  28. data/lib/super/badge.rb +60 -0
  29. data/lib/super/cheat.rb +6 -6
  30. data/lib/super/display/guesser.rb +1 -1
  31. data/lib/super/display/schema_types.rb +49 -1
  32. data/lib/super/display.rb +2 -1
  33. data/lib/super/error.rb +3 -0
  34. data/lib/super/filter/form_object.rb +74 -48
  35. data/lib/super/filter/guesser.rb +2 -0
  36. data/lib/super/filter/operator.rb +90 -64
  37. data/lib/super/filter/schema_types.rb +63 -80
  38. data/lib/super/filter.rb +1 -1
  39. data/lib/super/form/builder.rb +30 -39
  40. data/lib/super/form/field_transcript.rb +43 -0
  41. data/lib/super/form/guesser.rb +4 -4
  42. data/lib/super/form/schema_types.rb +66 -22
  43. data/lib/super/link.rb +2 -2
  44. data/lib/super/pagination.rb +2 -44
  45. data/lib/super/reset.rb +25 -0
  46. data/lib/super/schema.rb +4 -0
  47. data/lib/super/useful/builder.rb +4 -4
  48. data/lib/super/version.rb +1 -1
  49. data/lib/super.rb +3 -1
  50. data/lib/tasks/super/cheat.rake +1 -1
  51. metadata +25 -19
  52. data/app/views/super/application/_filter_type_select.html.erb +0 -21
  53. data/app/views/super/application/_filter_type_text.html.erb +0 -18
  54. data/app/views/super/application/_filter_type_timestamp.html.erb +0 -24
  55. data/app/views/super/application/_form_field_checkbox.html.erb +0 -1
  56. data/app/views/super/application/_form_field_flatpickr_date.html.erb +0 -8
  57. data/app/views/super/application/_form_field_flatpickr_datetime.html.erb +0 -8
  58. data/app/views/super/application/_form_field_flatpickr_time.html.erb +0 -8
  59. data/app/views/super/application/_form_field_rich_text_area.html.erb +0 -1
  60. data/app/views/super/application/_form_field_select.html.erb +0 -1
  61. data/app/views/super/application/_form_field_text.html.erb +0 -1
  62. data/app/views/super/feather/_chevron_down.html +0 -1
  63. data/docs/cheat.md +0 -41
  64. data/lib/super/controls/optional.rb +0 -113
  65. data/lib/super/controls/steps.rb +0 -106
  66. data/lib/super/controls/view.rb +0 -55
  67. data/lib/super/controls.rb +0 -22
@@ -2,112 +2,95 @@
2
2
 
3
3
  module Super
4
4
  class Filter
5
- # This schema type is used to configure the filtering form on your +#index+
6
- # action.
7
- #
8
- # The +operators:+ keyword argument can be left out in each case. There is
9
- # a default set of operators that are provided.
10
- #
11
5
  # Note: The constants under "Defined Under Namespace" are considered
12
6
  # private.
13
- #
14
- # class MemberDashboard
15
- # # ...
16
- #
17
- # def filter_schema
18
- # Super::Filter.new do |fields, type|
19
- # fields[:name] = type.text(operators: [
20
- # Super::Filter::Operator.eq,
21
- # Super::Filter::Operator.contain,
22
- # Super::Filter::Operator.ncontain,
23
- # Super::Filter::Operator.start,
24
- # Super::Filter::Operator.end,
25
- # ])
26
- # fields[:rank] = type.select(collection: Member.ranks.values)
27
- # fields[:position] = type.text(operators: [
28
- # Super::Filter::Operator.eq,
29
- # Super::Filter::Operator.neq,
30
- # Super::Filter::Operator.contain,
31
- # Super::Filter::Operator.ncontain,
32
- # ])
33
- # fields[:ship_id] = type.select(
34
- # collection: Ship.all.map { |s| ["#{s.name} (Ship ##{s.id})", s.id] },
35
- # )
36
- # fields[:created_at] = type.timestamp
37
- # fields[:updated_at] = type.timestamp
38
- # end
39
- # end
40
- #
41
- # # ...
42
- # end
43
7
  class SchemaTypes
44
- class Text
45
- def initialize(partial_path:, operators:)
46
- @partial_path = partial_path
47
- @operators = operators
48
- end
8
+ class OperatorList
9
+ include Enumerable
49
10
 
50
- attr_reader :operators
11
+ def initialize(*new_operators)
12
+ @operators = {}
13
+ @operator_transcript = {}
14
+ @fallback_transcript = nil
51
15
 
52
- def to_partial_path
53
- @partial_path
16
+ push(*new_operators)
54
17
  end
55
18
 
56
- def q
57
- [:q]
58
- end
59
- end
19
+ def push(*new_operators)
20
+ new_operators.flatten.map(&:dup).each do |new_operator|
21
+ new_identifier = new_operator.identifier.to_s
22
+
23
+ raise Error::AlreadyRegistered if @operators.key?(new_identifier)
60
24
 
61
- class Select
62
- def initialize(collection:, operators:)
63
- @collection = collection
64
- @operators = operators
25
+ @operators[new_identifier] = new_operator
26
+ end
27
+
28
+ nil
65
29
  end
66
30
 
67
- attr_reader :collection
68
- attr_reader :operators
31
+ alias add push
32
+
33
+ def each
34
+ return enum_for(:each) if !block_given?
69
35
 
70
- def to_partial_path
71
- "filter_type_select"
36
+ @operators.each do |identifier, operator|
37
+ yield(
38
+ OperatorWithFieldTranscript.new(
39
+ operator,
40
+ @operator_transcript[identifier] || @fallback_transcript
41
+ )
42
+ )
43
+ end
72
44
  end
73
45
 
74
- def q
75
- [:q]
46
+ def transcribe(operator_identifier = nil)
47
+ transcript = Form::FieldTranscript.new
48
+ yield transcript
49
+
50
+ if operator_identifier.nil?
51
+ @fallback_transcript = transcript
52
+ else
53
+ @operator_transcript[operator_identifier.to_s] = transcript
54
+ end
55
+
56
+ self
76
57
  end
77
58
  end
78
59
 
79
- class Timestamp
80
- def initialize(operators:)
81
- @operators = operators
60
+ class OperatorWithFieldTranscript
61
+ def initialize(operator, field_transcript)
62
+ @operator = operator
63
+ @field_transcript = field_transcript
82
64
  end
83
65
 
84
- attr_reader :operators
85
-
86
- def to_partial_path
87
- "filter_type_timestamp"
66
+ Super::Filter::Operator.instance_methods(false).each do |name|
67
+ delegate name, to: :@operator
88
68
  end
89
69
 
90
- def q
91
- [:q0, :q1]
92
- end
70
+ attr_reader :field_transcript
71
+ end
72
+
73
+ def use(*identifiers)
74
+ found_operators = identifiers.flatten.map { |id| Operator[id] }
75
+ OperatorList.new(*found_operators)
76
+ end
77
+
78
+ def select(collection)
79
+ use("eq", "null", "nnull")
80
+ .transcribe { |f| f.super.select(collection) }
93
81
  end
94
82
 
95
- def select(collection:, operators: Filter::Operator.select_defaults)
96
- Select.new(
97
- collection: collection,
98
- operators: operators
99
- )
83
+ def text
84
+ use("contain", "ncontain", "blank", "nblank")
100
85
  end
101
86
 
102
- def text(operators: Filter::Operator.text_defaults)
103
- Text.new(
104
- partial_path: "filter_type_text",
105
- operators: operators
106
- )
87
+ def timestamp
88
+ use("between", "null", "nnull")
89
+ .transcribe { |f| f.super.datetime_flatpickr }
107
90
  end
108
91
 
109
- def timestamp(operators: Filter::Operator.range_defaults)
110
- Timestamp.new(operators: operators)
92
+ def boolean
93
+ use("true", "false", "null", "nnull")
111
94
  end
112
95
  end
113
96
  end
data/lib/super/filter.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Super
4
4
  class Filter
5
5
  def initialize
6
- @schema_type = Filter::SchemaTypes.new
6
+ @schema_type = SchemaTypes.new
7
7
  @fields = Schema::Fields.new
8
8
 
9
9
  yield(@fields, @schema_type)
@@ -58,7 +58,7 @@ module Super
58
58
  @builder.check_box(attribute, options, checked_value, unchecked_value)
59
59
  end
60
60
 
61
- def flatpickr_date(attribute, options = {})
61
+ def date_flatpickr(attribute, options = {})
62
62
  options, defaults = split_defaults(
63
63
  options,
64
64
  class: "super-input w-full",
@@ -71,11 +71,13 @@ module Super
71
71
  )
72
72
  options[:class] = join_classes(defaults[:class], options[:class])
73
73
  options[:data] = defaults[:data].deep_merge(options[:data] || {})
74
+ options[:value] = @builder.object.public_send(attribute).presence
75
+ options[:value] = options[:value].iso8601 if options[:value].respond_to?(:iso8601)
74
76
 
75
77
  @builder.text_field(attribute, options)
76
78
  end
77
79
 
78
- def flatpickr_datetime(attribute, options = {})
80
+ def datetime_flatpickr(attribute, options = {})
79
81
  options, defaults = split_defaults(
80
82
  options,
81
83
  class: "super-input w-full",
@@ -90,11 +92,13 @@ module Super
90
92
  )
91
93
  options[:class] = join_classes(defaults[:class], options[:class])
92
94
  options[:data] = defaults[:data].deep_merge(options[:data] || {})
95
+ options[:value] = @builder.object.public_send(attribute).presence
96
+ options[:value] = options[:value].iso8601 if options[:value].respond_to?(:iso8601)
93
97
 
94
98
  @builder.text_field(attribute, options)
95
99
  end
96
100
 
97
- def flatpickr_time(attribute, options = {})
101
+ def time_flatpickr(attribute, options = {})
98
102
  options, defaults = split_defaults(
99
103
  options,
100
104
  class: "super-input w-full",
@@ -110,6 +114,8 @@ module Super
110
114
  )
111
115
  options[:class] = join_classes(defaults[:class], options[:class])
112
116
  options[:data] = defaults[:data].deep_merge(options[:data] || {})
117
+ options[:value] = @builder.object.public_send(attribute).presence
118
+ options[:value] = options[:value].strftime("%H:%M:%S") if options[:value].respond_to?(:strftime)
113
119
 
114
120
  @builder.text_field(attribute, options)
115
121
  end
@@ -131,25 +137,10 @@ module Super
131
137
  def select(attribute, choices, options = {}, html_options = {}, &block)
132
138
  options, defaults = split_defaults(options, include_blank: true)
133
139
  options = defaults.merge(options)
134
- html_options, html_defaults = split_defaults(html_options, class: "super-input super-input-select-field")
140
+ html_options, html_defaults = split_defaults(html_options, class: "super-input super-input-select")
135
141
  html_options[:class] = join_classes(html_defaults[:class], html_options[:class])
136
142
 
137
- parts = [
138
- %(<div class="super-input-select">).html_safe,
139
- @builder.select(attribute, choices, options, html_options, &block),
140
- <<~HTML.html_safe,
141
- <div class="super-input-select-icon text-blue-700">
142
- <span class="h-4 w-4">
143
- HTML
144
- @template.render("super/feather/chevron_down"),
145
- <<~HTML.html_safe,
146
- </span>
147
- </div>
148
- HTML
149
- %(</div>).html_safe,
150
- ]
151
-
152
- @template.safe_join(parts)
143
+ @builder.select(attribute, choices, options, html_options, &block)
153
144
  end
154
145
 
155
146
  def submit(value = nil, options = {})
@@ -171,60 +162,60 @@ module Super
171
162
  @template.content_tag(:div, class: "super-field-group", &block)
172
163
  end
173
164
 
174
- def check_box!(attribute, checked_value = "1", unchecked_value = "0", label: {}, field: {}, show_errors: true)
165
+ def check_box!(attribute, checked_value: "1", unchecked_value: "0", label_text: nil, label: {}, field: {}, show_errors: true)
175
166
  label[:super] ||= {}
176
167
  label[:super] = { class: "select-none ml-1" }.merge(label[:super])
177
168
  container do
178
169
  compact_join([
179
170
  "<div>".html_safe,
180
171
  public_send(:check_box, attribute, field, checked_value, unchecked_value),
181
- public_send(:label, attribute, nil, label),
172
+ public_send(:label, attribute, label_text, label),
182
173
  "</div>".html_safe,
183
174
  show_errors && inline_errors(attribute),
184
175
  ])
185
176
  end
186
177
  end
187
178
 
188
- def flatpickr_date!(attribute, label: {}, field: {}, show_errors: true)
179
+ def date_flatpickr!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
189
180
  container do
190
181
  compact_join([
191
- public_send(:label, attribute, label),
182
+ public_send(:label, attribute, label_text, label),
192
183
  %(<div class="mt-1">).html_safe,
193
- public_send(:flatpickr_date, attribute, field),
184
+ public_send(:date_flatpickr, attribute, field),
194
185
  show_errors && inline_errors(attribute),
195
186
  %(</div>).html_safe,
196
187
  ])
197
188
  end
198
189
  end
199
190
 
200
- def flatpickr_datetime!(attribute, label: {}, field: {}, show_errors: true)
191
+ def datetime_flatpickr!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
201
192
  container do
202
193
  compact_join([
203
- public_send(:label, attribute, label),
194
+ public_send(:label, attribute, label_text, label),
204
195
  %(<div class="mt-1">).html_safe,
205
- public_send(:flatpickr_datetime, attribute, field),
196
+ public_send(:datetime_flatpickr, attribute, field),
206
197
  show_errors && inline_errors(attribute),
207
198
  %(</div>).html_safe,
208
199
  ])
209
200
  end
210
201
  end
211
202
 
212
- def flatpickr_time!(attribute, label: {}, field: {}, show_errors: true)
203
+ def time_flatpickr!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
213
204
  container do
214
205
  compact_join([
215
- public_send(:label, attribute, label),
206
+ public_send(:label, attribute, label_text, label),
216
207
  %(<div class="mt-1">).html_safe,
217
- public_send(:flatpickr_time, attribute, field),
208
+ public_send(:time_flatpickr, attribute, field),
218
209
  show_errors && inline_errors(attribute),
219
210
  %(</div>).html_safe,
220
211
  ])
221
212
  end
222
213
  end
223
214
 
224
- def password_field!(attribute, label: {}, field: {}, show_errors: true)
215
+ def password_field!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
225
216
  container do
226
217
  compact_join([
227
- public_send(:label, attribute, label),
218
+ public_send(:label, attribute, label_text, label),
228
219
  %(<div class="mt-1">).html_safe,
229
220
  public_send(:password_field, attribute, field),
230
221
  show_errors && inline_errors(attribute),
@@ -233,10 +224,10 @@ module Super
233
224
  end
234
225
  end
235
226
 
236
- def rich_text_area!(attribute, label: {}, field: {}, show_errors: true)
227
+ def rich_text_area!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
237
228
  container do
238
229
  compact_join([
239
- public_send(:label, attribute, label),
230
+ public_send(:label, attribute, label_text, label),
240
231
  %(<div class="mt-1">).html_safe,
241
232
  public_send(:rich_text_area, attribute, field),
242
233
  show_errors && inline_errors(attribute),
@@ -245,10 +236,10 @@ module Super
245
236
  end
246
237
  end
247
238
 
248
- def select!(attribute, collection, label: {}, field: {}, show_errors: true)
239
+ def select!(attribute, collection, label_text: nil, label: {}, field: {}, show_errors: true)
249
240
  container do
250
241
  compact_join([
251
- public_send(:label, attribute, label),
242
+ public_send(:label, attribute, label_text, label),
252
243
  %(<div class="mt-1">).html_safe,
253
244
  public_send(:select, attribute, collection, field),
254
245
  show_errors && inline_errors(attribute),
@@ -257,10 +248,10 @@ module Super
257
248
  end
258
249
  end
259
250
 
260
- def text_field!(attribute, label: {}, field: {}, show_errors: true)
251
+ def text_field!(attribute, label_text: nil, label: {}, field: {}, show_errors: true)
261
252
  container do
262
253
  compact_join([
263
- public_send(:label, attribute, label),
254
+ public_send(:label, attribute, label_text, label),
264
255
  %(<div class="mt-1">).html_safe,
265
256
  public_send(:text_field, attribute, field),
266
257
  show_errors && inline_errors(attribute),
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ class Form
5
+ # Holds a recording of a form field definition
6
+ class FieldTranscript
7
+ def initialize
8
+ @super = false
9
+ end
10
+
11
+ attr_reader :method_name
12
+ attr_reader :args
13
+ attr_reader :kwargs
14
+
15
+ def super
16
+ @super = true
17
+ self
18
+ end
19
+
20
+ def super?
21
+ @super
22
+ end
23
+
24
+ def method_missing(new_method_name, *args, **kwargs)
25
+ if @method_name.present?
26
+ method_chain =
27
+ if super?
28
+ "super.#{@method_name}"
29
+ else
30
+ @method_name.to_s
31
+ end
32
+
33
+ raise Error::AlreadyTranscribed, "This instance already holds a transcription for: #{method_chain}"
34
+ end
35
+
36
+ @method_name = new_method_name
37
+ @args = args
38
+ @kwargs = kwargs
39
+ @method_name
40
+ end
41
+ end
42
+ end
43
+ end
@@ -24,13 +24,13 @@ module Super
24
24
  def attribute_type_for(attribute_name)
25
25
  case @model.type_for_attribute(attribute_name).type
26
26
  when :datetime
27
- @type.flatpickr_datetime
27
+ @type.datetime_flatpickr
28
28
  when :time
29
- @type.flatpickr_time
29
+ @type.time_flatpickr
30
30
  when :date
31
- @type.flatpickr_date
31
+ @type.date_flatpickr
32
32
  else
33
- @type.string
33
+ @type.text_field
34
34
  end
35
35
  end
36
36
  end
@@ -3,6 +3,38 @@
3
3
  module Super
4
4
  class Form
5
5
  class SchemaTypes
6
+ class Direct
7
+ def initialize(super_builder:, method_name:, args:, kwargs:)
8
+ @super_builder = super_builder
9
+ @method_name = method_name
10
+ @args = args
11
+ @kwargs = kwargs
12
+ end
13
+
14
+ attr_reader :super_builder
15
+ attr_reader :method_name
16
+ attr_reader :args
17
+ attr_reader :kwargs
18
+
19
+ def nested_fields
20
+ {}
21
+ end
22
+
23
+ def to_partial_path
24
+ "form_field"
25
+ end
26
+
27
+ def ==(other)
28
+ return false if other.class != self.class
29
+ return false if other.super_builder != super_builder
30
+ return false if other.method_name != method_name
31
+ return false if other.args != args
32
+ return false if other.kwargs != kwargs
33
+
34
+ true
35
+ end
36
+ end
37
+
6
38
  class Generic
7
39
  def initialize(partial_path:, extras:, nested:)
8
40
  @partial_path = partial_path
@@ -65,61 +97,73 @@ module Super
65
97
  @fields = fields
66
98
  end
67
99
 
68
- def generic(partial_path, **extras)
100
+ def partial(partial_path, **extras)
69
101
  Generic.new(partial_path: partial_path, extras: extras, nested: {})
70
102
  end
71
103
 
72
- def select(**extras)
73
- Generic.new(partial_path: "form_field_select", extras: extras, nested: {})
104
+ def direct(method_name, *args, super_builder: true, **kwargs)
105
+ Direct.new(super_builder: super_builder, method_name: method_name, args: args, kwargs: kwargs)
74
106
  end
75
107
 
76
- def string(**extras)
77
- Generic.new(partial_path: "form_field_text", extras: extras, nested: {})
108
+ def select(*args, **kwargs)
109
+ Direct.new(super_builder: true, method_name: :select!, args: args, kwargs: kwargs)
78
110
  end
79
111
 
80
- alias text string
112
+ def text_field(*args, **kwargs)
113
+ Direct.new(super_builder: true, method_name: :text_field!, args: args, kwargs: kwargs)
114
+ end
115
+
116
+ def rich_text_area(*args, **kwargs)
117
+ Direct.new(super_builder: true, method_name: :rich_text_area!, args: args, kwargs: kwargs)
118
+ end
119
+
120
+ def check_box(*args, **kwargs)
121
+ Direct.new(super_builder: true, method_name: :check_box!, args: args, kwargs: kwargs)
122
+ end
81
123
 
82
- def rich_text_area(**extras)
83
- Generic.new(partial_path: "form_field_rich_text_area", extras: extras, nested: {})
124
+ def date_flatpickr(*args, **kwargs)
125
+ Direct.new(super_builder: true, method_name: :date_flatpickr!, args: args, kwargs: kwargs)
84
126
  end
85
127
 
86
- def checkbox(**extras)
87
- Generic.new(partial_path: "form_field_checkbox", extras: extras, nested: {})
128
+ def datetime_flatpickr(*args, **kwargs)
129
+ Direct.new(super_builder: true, method_name: :datetime_flatpickr!, args: args, kwargs: kwargs)
88
130
  end
89
131
 
90
- def flatpickr_date(**extras)
91
- Generic.new(partial_path: "form_field_flatpickr_date", extras: extras, nested: {})
132
+ def hidden_field(*args, **kwargs)
133
+ Direct.new(super_builder: false, method_name: :hidden_field, args: args, kwargs: kwargs)
92
134
  end
93
135
 
94
- def flatpickr_datetime(**extras)
95
- Generic.new(partial_path: "form_field_flatpickr_datetime", extras: extras, nested: {})
136
+ def password_field(*args, **kwargs)
137
+ Direct.new(super_builder: true, method_name: :password_field!, args: args, kwargs: kwargs)
96
138
  end
97
139
 
98
- def flatpickr_time(**extras)
99
- Generic.new(partial_path: "form_field_flatpickr_time", extras: extras, nested: {})
140
+ def time_flatpickr(*args, **kwargs)
141
+ Direct.new(super_builder: true, method_name: :time_flatpickr!, args: args, kwargs: kwargs)
100
142
  end
101
143
 
102
144
  def has_many(reader, **extras)
103
- nested = @fields.nested do
104
- yield
145
+ subfields = Schema::Fields.new
146
+ @fields.nested do
147
+ yield subfields
105
148
  end
106
149
 
107
150
  Generic.new(
108
151
  partial_path: "form_has_many",
109
152
  extras: extras.merge(reader: reader),
110
- nested: nested
153
+ nested: subfields.to_h
111
154
  )
112
155
  end
113
156
 
114
157
  def has_one(reader, **extras)
115
- nested = @fields.nested do
116
- yield
158
+ subfields = Schema::Fields.new
159
+ @fields.nested do
160
+ yield subfields
117
161
  end
118
162
 
119
163
  Generic.new(
120
164
  partial_path: "form_has_one",
121
165
  extras: extras.merge(reader: reader),
122
- nested: nested
166
+ nested: subfields.to_h
123
167
  )
124
168
  end
125
169