view_partial_form_builder 0.1.4 → 0.2.2
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.md +64 -64
- data/lib/view_partial_form_builder/engine.rb +10 -2
- data/lib/view_partial_form_builder/form_builder.rb +6 -304
- data/lib/view_partial_form_builder/lookup_override.rb +8 -12
- data/lib/view_partial_form_builder/template_proxy.rb +120 -0
- data/lib/view_partial_form_builder/version.rb +1 -1
- data/lib/view_partial_form_builder.rb +5 -1
- metadata +14 -42
- data/lib/view_partial_form_builder/html_attributes.rb +0 -47
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c09b26fa824aff0b6fc36212577a96ca2daecefdf5dda0614ab000ea5882d4a3
|
4
|
+
data.tar.gz: dfaccd8fd95ec8049e4518079b67af9962fa1b2ffb28125233f400f109bf054f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f3381c4245c6bd84e61c79d3d28b9ff195a4fa632f657be10153b7c58af1ce29ff2253556858450a9464b6b2b39a82aab83e53cc1327a046d77b2f21348be48
|
7
|
+
data.tar.gz: e0e2f622b47ed7bd4db4873f5864746c0663d64fed8583475a75ab4744b60567edcad91156cf721921aa4ee36d5a370a4116efd5b7c0b06839c597ae99825d40
|
data/README.md
CHANGED
@@ -37,7 +37,7 @@ Next, declare view partials that correspond to the [`FormBuilder`][FormBuilder]
|
|
37
37
|
helper method you'd like to have more control over:
|
38
38
|
|
39
39
|
```html+erb
|
40
|
-
<%# app/views/form_builder/_text_field.html.erb %>
|
40
|
+
<%# app/views/application/form_builder/_text_field.html.erb %>
|
41
41
|
|
42
42
|
<input
|
43
43
|
type="text"
|
@@ -48,7 +48,7 @@ helper method you'd like to have more control over:
|
|
48
48
|
<% end %>
|
49
49
|
>
|
50
50
|
|
51
|
-
<%# app/views/form_builder/_email_field.html.erb %>
|
51
|
+
<%# app/views/application/form_builder/_email_field.html.erb %>
|
52
52
|
|
53
53
|
<input
|
54
54
|
type="email"
|
@@ -59,7 +59,7 @@ helper method you'd like to have more control over:
|
|
59
59
|
<% end %>
|
60
60
|
>
|
61
61
|
|
62
|
-
<%# app/views/form_builder/_button.html.erb %>
|
62
|
+
<%# app/views/application/form_builder/_button.html.erb %>
|
63
63
|
|
64
64
|
<button
|
65
65
|
class="button button--primary"
|
@@ -76,7 +76,7 @@ You'll have local access to the `FormBuilder` instance as the template-local
|
|
76
76
|
generating HTML through Rails' helpers:
|
77
77
|
|
78
78
|
```html+erb
|
79
|
-
<%# app/views/form_builder/_email_field.html.erb %>
|
79
|
+
<%# app/views/application/form_builder/_email_field.html.erb %>
|
80
80
|
|
81
81
|
<div class="email-field-wrapper">
|
82
82
|
<%= form.email_field(method, required: true, **options)) %>
|
@@ -84,10 +84,10 @@ generating HTML through Rails' helpers:
|
|
84
84
|
```
|
85
85
|
|
86
86
|
```html+erb
|
87
|
-
<%# app/views/form_builder/_button.html.erb %>
|
87
|
+
<%# app/views/application/form_builder/_button.html.erb %>
|
88
88
|
|
89
89
|
<div class="button-wrapper">
|
90
|
-
<%= form.button(
|
90
|
+
<%= form.button(value, options, &block) %>
|
91
91
|
</div>
|
92
92
|
```
|
93
93
|
|
@@ -116,30 +116,8 @@ In addition, each view partial receives:
|
|
116
116
|
* `form` - a reference to the instance of `ViewPartialFormBuilder`, which is a
|
117
117
|
descendant of [`ActionView::Helpers::FormBuilder`][FormBuilder]
|
118
118
|
|
119
|
-
* `
|
120
|
-
|
121
|
-
helper by [splatting them][splat] out.
|
122
|
-
|
123
|
-
* `&block` - a callable, `yield`-able block if the helper method was passed one
|
124
|
-
|
125
|
-
In cases when a [`ActionView::Helpers::FormBuilder` helper
|
126
|
-
method][FormBuilder]'s last arguments are options (either `Hash` instances or
|
127
|
-
[keyword arguments][]), they're omitted from the `arguments` array.
|
128
|
-
|
129
|
-
If you want to pass-through all arguments, options, and block parameters, you
|
130
|
-
can splat them out:
|
131
|
-
|
132
|
-
```html+erb
|
133
|
-
<%# app/views/form_builder/_label.html.erb %>
|
134
|
-
|
135
|
-
<%= form.label(*arguments, **options, &block) %>
|
136
|
-
```
|
137
|
-
|
138
|
-
```html+erb
|
139
|
-
<%# app/views/form_builder/_select.html.erb %>
|
140
|
-
|
141
|
-
<%= form.select(*arguments, **html_options, &block) %>
|
142
|
-
```
|
119
|
+
* `block` - the block if the helper method was passed one. Forward it along to
|
120
|
+
field helpers as `&block`.
|
143
121
|
|
144
122
|
#### Handling DOMTokenList attributes
|
145
123
|
|
@@ -158,16 +136,12 @@ When rendering a field's DOMTokenList-backed attributes (like `class` or
|
|
158
136
|
controllers][stimulus-controller]), transforming and combining singular `String`
|
159
137
|
instances into lists of token can be very useful.
|
160
138
|
|
161
|
-
To simplify those scenarios, a partial's template-local optional attributes are
|
162
|
-
made available with the `#merge_token_lists` method.
|
163
|
-
|
164
139
|
These optional attributes are available through the `options` or `html_options`
|
165
140
|
partial-local variables. Their name will depend on the partial's corresponding
|
166
141
|
[`ActionView::Helpers::FormBuilder`][FormBuilder] interface.
|
167
142
|
|
168
|
-
|
169
|
-
|
170
|
-
Given call to `form.text_field` and a corresponding partial declaration:
|
143
|
+
To "merge" attributes together, you can combine Ruby's `String` interpolation
|
144
|
+
and `Hash#delete`:
|
171
145
|
|
172
146
|
```html+erb
|
173
147
|
<%# app/views/users/new.html.erb %>
|
@@ -175,9 +149,13 @@ Given call to `form.text_field` and a corresponding partial declaration:
|
|
175
149
|
<%= form.text_field(:name, class: "text-field--modifier") %>
|
176
150
|
<% end %>
|
177
151
|
|
178
|
-
<# app/views/form_builder/_text_field.html.erb %>
|
152
|
+
<# app/views/application/form_builder/_text_field.html.erb %>
|
179
153
|
|
180
|
-
<%= form.text_field(
|
154
|
+
<%= form.text_field(
|
155
|
+
method,
|
156
|
+
class: "text-field #{options.delete(:class)}",
|
157
|
+
**options
|
158
|
+
) %>
|
181
159
|
```
|
182
160
|
|
183
161
|
The resulting HTML `<input>` element will merge have its [`class`
|
@@ -192,7 +170,6 @@ values:
|
|
192
170
|
[button]: https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-button
|
193
171
|
[local_assigns]: https://api.rubyonrails.org/classes/ActionView/Template.html#method-i-local_assigns
|
194
172
|
[splat]: https://ruby-doc.org/core-2.2.0/doc/syntax/calling_methods_rdoc.html#label-Array+to+Arguments+Conversion
|
195
|
-
[keyword arguments]: https://thoughtbot.com/blog/ruby-2-keyword-arguments
|
196
173
|
[mdn-class]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
|
197
174
|
[DOMTokenList]: https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList
|
198
175
|
[classList]: https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
|
@@ -210,7 +187,7 @@ block-local `form` variable:
|
|
210
187
|
```erb
|
211
188
|
<%# app/views/users/form_builder/_email_field.html.erb %>
|
212
189
|
|
213
|
-
<%= form.default.email_field(
|
190
|
+
<%= form.default.email_field(method, options) %>
|
214
191
|
```
|
215
192
|
|
216
193
|
When passing a `model:` or `scope:` to calls to [`form_with`][form_with],
|
@@ -219,44 +196,64 @@ look up path.
|
|
219
196
|
|
220
197
|
For example, when calling `form_with(model: User.new)`, a partial declared in
|
221
198
|
`app/views/users/form_builder/` would take precedent over a partial declared in
|
222
|
-
`app/views/form_builder/`.
|
199
|
+
`app/views/application/form_builder/`.
|
223
200
|
|
224
201
|
```erb
|
225
202
|
<%# app/views/users/form_builder/_password_field.html.erb %>
|
226
203
|
|
227
204
|
<div class="password-field-wrapper">
|
228
|
-
<%= form.password_field(
|
205
|
+
<%= form.password_field(method, options) %>
|
229
206
|
</div>
|
230
207
|
```
|
231
208
|
|
232
|
-
If you'd like to render a specific partial for a field,
|
233
|
-
|
209
|
+
If you'd like to render a specific partial for a field, make sure that you pass
|
210
|
+
along the `form:` (along with any other partial-local variables) as part of the
|
211
|
+
`render` call's `locals:` option:
|
212
|
+
|
234
213
|
|
235
214
|
```erb
|
236
215
|
<%# app/views/users/new.html.erb %>
|
237
216
|
|
238
217
|
<%= form_with(model: User.new) do |form| %>
|
239
|
-
<%=
|
218
|
+
<%= render("emails/my_special_email_field", {
|
219
|
+
form: form,
|
220
|
+
method: :email,
|
221
|
+
options: { class: "user-email" },
|
222
|
+
) %>
|
240
223
|
<% end %>
|
224
|
+
|
225
|
+
<%# app/views/emails/_my_special_email_field.html.erb %>
|
226
|
+
|
227
|
+
<%= form.email_field(
|
228
|
+
method,
|
229
|
+
class: "my-special-email #{options.delete(:class)},
|
230
|
+
**options
|
231
|
+
) %>
|
241
232
|
```
|
242
233
|
|
243
234
|
#### Composing partials
|
244
235
|
|
245
|
-
|
246
|
-
|
247
|
-
with a consumer facing site, but has
|
236
|
+
Layering partials on top of one another can be useful to share foundational
|
237
|
+
styles and configuration across your fields. For instance, consider an
|
238
|
+
administrative interface that shares styles with a consumer facing site, but has
|
239
|
+
additional bells and whistles.
|
248
240
|
|
249
241
|
Declare the consumer facing inputs (in this example, `<input type="search">`):
|
250
242
|
|
251
243
|
```html+erb
|
252
|
-
<%# app/views/form_builder/_search_field.html.erb %>
|
244
|
+
<%# app/views/application/form_builder/_search_field.html.erb %>
|
253
245
|
|
254
246
|
<%= form.search_field(
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
247
|
+
method,
|
248
|
+
class: "
|
249
|
+
search-field
|
250
|
+
#{options.delete(:class)}
|
251
|
+
",
|
252
|
+
"data-controller": "
|
253
|
+
input->search#executeQuery
|
254
|
+
#{options.delete(:"data-controller")}
|
255
|
+
",
|
256
|
+
**options
|
260
257
|
) %>
|
261
258
|
```
|
262
259
|
|
@@ -264,20 +261,23 @@ Then, declare the administrative interface's inputs, in terms of overriding the
|
|
264
261
|
foundation built by the more general definitions:
|
265
262
|
|
266
263
|
```html+erb
|
267
|
-
<%# app/views/admin/form_builder/_search_field.html.erb %>
|
264
|
+
<%# app/views/admin/application/form_builder/_search_field.html.erb %>
|
268
265
|
|
269
266
|
<%= form.search_field(
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
class
|
274
|
-
|
275
|
-
|
267
|
+
method,
|
268
|
+
class: "
|
269
|
+
search-field--admin
|
270
|
+
#{options.delete(:class}
|
271
|
+
",
|
272
|
+
"data-controller": "
|
273
|
+
focus->admin-search#clearResults
|
274
|
+
#{options.delete(:"data-controller")}
|
275
|
+
",
|
276
276
|
) %>
|
277
277
|
```
|
278
278
|
|
279
|
-
The rendered `admin/form_builder/search_field` partial combines
|
280
|
-
arguments from both partials:
|
279
|
+
The rendered `admin/application/form_builder/search_field` partial combines
|
280
|
+
options and arguments from both partials:
|
281
281
|
|
282
282
|
```html
|
283
283
|
<input
|
@@ -327,7 +327,7 @@ Models declared within modules will be delimited with `/`. For example,
|
|
327
327
|
### Configuration
|
328
328
|
|
329
329
|
View partials lookup and resolution will be scoped to the
|
330
|
-
`app/views/form_builder` directory.
|
330
|
+
`app/views/application/form_builder` directory.
|
331
331
|
|
332
332
|
To override this destination to another directory (for example,
|
333
333
|
`app/views/fields`, or `app/views/users/fields`), set
|
@@ -1,9 +1,17 @@
|
|
1
|
-
require "view_partial_form_builder/form_builder"
|
2
|
-
|
3
1
|
module ViewPartialFormBuilder
|
4
2
|
class Engine < ::Rails::Engine
|
5
3
|
ActiveSupport.on_load(:action_controller_base) do
|
6
4
|
default_form_builder ViewPartialFormBuilder::FormBuilder
|
7
5
|
end
|
6
|
+
|
7
|
+
ActiveSupport.on_load(:view_partial_form_builder) do
|
8
|
+
if Rails::VERSION::MAJOR > 7
|
9
|
+
self.aliased_field_helpers = {
|
10
|
+
checkbox: [:check_box],
|
11
|
+
collection_checkboxes: [:collection_check_boxes],
|
12
|
+
rich_textarea: [:rich_text_area]
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
8
16
|
end
|
9
17
|
end
|
@@ -1,313 +1,15 @@
|
|
1
|
-
require "view_partial_form_builder/lookup_override"
|
2
|
-
require "view_partial_form_builder/html_attributes"
|
3
|
-
|
4
1
|
module ViewPartialFormBuilder
|
5
2
|
class FormBuilder < ActionView::Helpers::FormBuilder
|
3
|
+
class_attribute :aliased_field_helpers, default: {}
|
4
|
+
|
6
5
|
attr_reader :default
|
7
6
|
|
8
7
|
def initialize(*)
|
9
8
|
super
|
10
|
-
|
11
|
-
@
|
12
|
-
object_name,
|
13
|
-
object,
|
14
|
-
@template,
|
15
|
-
options,
|
16
|
-
)
|
17
|
-
@lookup_override = LookupOverride.new(
|
18
|
-
prefixes: @template.lookup_context.prefixes,
|
19
|
-
object_name: object&.model_name || object_name,
|
20
|
-
view_partial_directory: ViewPartialFormBuilder.view_partial_directory,
|
21
|
-
)
|
22
|
-
end
|
23
|
-
|
24
|
-
def label(method, text = nil, **options, &block)
|
25
|
-
locals = {
|
26
|
-
method: method,
|
27
|
-
text: text,
|
28
|
-
options: HtmlAttributes.new(options),
|
29
|
-
block: block,
|
30
|
-
arguments: [method, text],
|
31
|
-
}
|
32
|
-
|
33
|
-
render_partial("label", locals, fallback: -> { super }, &block)
|
34
|
-
end
|
35
|
-
|
36
|
-
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
|
37
|
-
locals = {
|
38
|
-
method: method,
|
39
|
-
options: HtmlAttributes.new(options),
|
40
|
-
checked_value: checked_value,
|
41
|
-
unchecked_value: unchecked_value,
|
42
|
-
arguments: [method, options, checked_value, unchecked_value],
|
43
|
-
}
|
44
|
-
|
45
|
-
render_partial("check_box", locals, fallback: -> { super })
|
46
|
-
end
|
47
|
-
|
48
|
-
def radio_button(method, tag_value, **options)
|
49
|
-
locals = {
|
50
|
-
method: method,
|
51
|
-
tag_value: tag_value,
|
52
|
-
options: HtmlAttributes.new(options),
|
53
|
-
arguments: [method, tag_value],
|
54
|
-
}
|
55
|
-
|
56
|
-
render_partial("radio_button", locals, fallback: -> { super })
|
57
|
-
end
|
58
|
-
|
59
|
-
def select(method, choices = nil, options = {}, **html_options, &block)
|
60
|
-
html_options = @default_html_options.merge(html_options)
|
61
|
-
|
62
|
-
locals = {
|
63
|
-
method: method,
|
64
|
-
choices: choices,
|
65
|
-
options: options,
|
66
|
-
html_options: HtmlAttributes.new(html_options),
|
67
|
-
block: block,
|
68
|
-
arguments: [method, choices, options],
|
69
|
-
}
|
70
|
-
|
71
|
-
render_partial("select", locals, fallback: -> { super }, &block)
|
72
|
-
end
|
73
|
-
|
74
|
-
def collection_select(method, collection, value_method, text_method, options = {}, **html_options)
|
75
|
-
html_options = @default_html_options.merge(html_options)
|
76
|
-
|
77
|
-
locals = {
|
78
|
-
method: method,
|
79
|
-
collection: collection,
|
80
|
-
value_method: value_method,
|
81
|
-
text_method: text_method,
|
82
|
-
options: options,
|
83
|
-
html_options: html_options,
|
84
|
-
arguments: [method, collection, value_method, text_method, options],
|
85
|
-
}
|
86
|
-
|
87
|
-
render_partial("collection_select", locals, fallback: -> { super })
|
88
|
-
end
|
89
|
-
|
90
|
-
def collection_check_boxes(method, collection, value_method, text_method, options = {}, **html_options, &block)
|
91
|
-
html_options = @default_html_options.merge(html_options)
|
92
|
-
|
93
|
-
locals = {
|
94
|
-
method: method,
|
95
|
-
collection: collection,
|
96
|
-
value_method: value_method,
|
97
|
-
text_method: text_method,
|
98
|
-
options: options,
|
99
|
-
html_options: HtmlAttributes.new(html_options),
|
100
|
-
block: block,
|
101
|
-
arguments: [
|
102
|
-
method,
|
103
|
-
collection,
|
104
|
-
value_method,
|
105
|
-
text_method,
|
106
|
-
options,
|
107
|
-
],
|
108
|
-
}
|
109
|
-
|
110
|
-
render_partial("collection_check_boxes", locals, fallback: -> { super }, &block)
|
111
|
-
end
|
112
|
-
|
113
|
-
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, **html_options, &block)
|
114
|
-
html_options = @default_html_options.merge(html_options)
|
115
|
-
|
116
|
-
locals = {
|
117
|
-
method: method,
|
118
|
-
collection: collection,
|
119
|
-
value_method: value_method,
|
120
|
-
text_method: text_method,
|
121
|
-
options: options,
|
122
|
-
html_options: HtmlAttributes.new(html_options),
|
123
|
-
block: block,
|
124
|
-
arguments: [
|
125
|
-
method,
|
126
|
-
collection,
|
127
|
-
value_method,
|
128
|
-
text_method,
|
129
|
-
options,
|
130
|
-
],
|
131
|
-
}
|
132
|
-
|
133
|
-
render_partial("collection_radio_buttons", locals, fallback: -> { super }, &block)
|
134
|
-
end
|
135
|
-
|
136
|
-
def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, **html_options)
|
137
|
-
html_options = @default_html_options.merge(html_options)
|
138
|
-
|
139
|
-
locals = {
|
140
|
-
method: method,
|
141
|
-
collection: collection,
|
142
|
-
group_method: group_method,
|
143
|
-
group_label_method: group_label_method,
|
144
|
-
option_key_method: option_key_method,
|
145
|
-
option_value_method: option_value_method,
|
146
|
-
html_options: HtmlAttributes.new(html_options),
|
147
|
-
options: options,
|
148
|
-
arguments: [
|
149
|
-
method,
|
150
|
-
collection,
|
151
|
-
group_method,
|
152
|
-
group_label_method,
|
153
|
-
option_key_method,
|
154
|
-
option_value_method,
|
155
|
-
options,
|
156
|
-
],
|
157
|
-
}
|
158
|
-
|
159
|
-
render_partial("grouped_collection_select", locals, fallback: -> { super })
|
160
|
-
end
|
161
|
-
|
162
|
-
def time_zone_select(method, priority_zones = nil, options = {}, **html_options)
|
163
|
-
html_options = @default_html_options.merge(html_options)
|
164
|
-
|
165
|
-
locals = {
|
166
|
-
method: method,
|
167
|
-
priority_zones: priority_zones,
|
168
|
-
html_options: HtmlAttributes.new(html_options),
|
169
|
-
options: options,
|
170
|
-
arguments: [method, priority_zones, options],
|
171
|
-
}
|
172
|
-
|
173
|
-
render_partial("time_zone_select", locals, fallback: -> { super })
|
174
|
-
end
|
175
|
-
|
176
|
-
def date_select(method, options = {}, **html_options)
|
177
|
-
locals = {
|
178
|
-
method: method,
|
179
|
-
options: options,
|
180
|
-
html_options: HtmlAttributes.new(html_options),
|
181
|
-
arguments: [method, options, html_options],
|
182
|
-
}
|
183
|
-
|
184
|
-
render_partial("date_select", locals, fallback: -> { super })
|
185
|
-
end
|
186
|
-
|
187
|
-
def hidden_field(method, **options)
|
188
|
-
@emitted_hidden_id = true if method == :id
|
189
|
-
|
190
|
-
locals = {
|
191
|
-
method: method,
|
192
|
-
options: HtmlAttributes.new(options),
|
193
|
-
arguments: [method],
|
194
|
-
}
|
195
|
-
|
196
|
-
render_partial("hidden_field", locals, fallback: -> { super })
|
197
|
-
end
|
198
|
-
|
199
|
-
def file_field(method, **options)
|
200
|
-
self.multipart = true
|
201
|
-
|
202
|
-
locals = {
|
203
|
-
method: method,
|
204
|
-
options: HtmlAttributes.new(options),
|
205
|
-
arguments: [method],
|
206
|
-
}
|
207
|
-
|
208
|
-
render_partial("file_field", locals, fallback: -> { super })
|
209
|
-
end
|
210
|
-
|
211
|
-
def submit(value = nil, **options)
|
212
|
-
value, options = nil, value if value.is_a?(Hash)
|
213
|
-
value ||= submit_default_value
|
214
|
-
|
215
|
-
locals = {
|
216
|
-
value: value,
|
217
|
-
options: HtmlAttributes.new(options),
|
218
|
-
arguments: [value],
|
219
|
-
}
|
220
|
-
|
221
|
-
render_partial("submit", locals, fallback: -> { super })
|
222
|
-
end
|
223
|
-
|
224
|
-
def button(value = nil, **options)
|
225
|
-
value, options = nil, value if value.is_a?(Hash)
|
226
|
-
value ||= submit_default_value
|
227
|
-
|
228
|
-
locals = {
|
229
|
-
value: value,
|
230
|
-
options: HtmlAttributes.new(options),
|
231
|
-
arguments: [value],
|
232
|
-
}
|
233
|
-
|
234
|
-
render_partial("button", locals, fallback: -> { super })
|
235
|
-
end
|
236
|
-
|
237
|
-
DYNAMICALLY_DECLARED = (
|
238
|
-
field_helpers +
|
239
|
-
[:rich_text_area] -
|
240
|
-
[:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]
|
241
|
-
)
|
242
|
-
|
243
|
-
DYNAMICALLY_DECLARED.each do |selector|
|
244
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
245
|
-
def #{selector}(method, **options)
|
246
|
-
render_partial(
|
247
|
-
"#{selector}",
|
248
|
-
{
|
249
|
-
method: method,
|
250
|
-
options: HtmlAttributes.new(options),
|
251
|
-
arguments: [method],
|
252
|
-
},
|
253
|
-
fallback: -> { super },
|
254
|
-
)
|
255
|
-
end
|
256
|
-
RUBY
|
257
|
-
end
|
258
|
-
|
259
|
-
private
|
260
|
-
|
261
|
-
def render_partial(field, locals, fallback:, &block)
|
262
|
-
options = locals.fetch(:options, {})
|
263
|
-
partial_override = options.delete(:partial)
|
264
|
-
locals = objectify_options(options).merge(locals, form: self)
|
265
|
-
|
266
|
-
partial = if partial_override.present?
|
267
|
-
find_partial(partial_override, locals, prefixes: [])
|
268
|
-
else
|
269
|
-
find_partial(field, locals, prefixes: prefixes_after(field))
|
270
|
-
end
|
271
|
-
|
272
|
-
if partial.nil? || about_to_recurse_infinitely?(partial)
|
273
|
-
fallback.call
|
274
|
-
else
|
275
|
-
partial.render(@template, locals, &block)
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
def find_partial(template_name, locals, prefixes:)
|
280
|
-
template_is_partial = true
|
281
|
-
|
282
|
-
@template.lookup_context.find_all(
|
283
|
-
template_name,
|
284
|
-
prefixes,
|
285
|
-
template_is_partial,
|
286
|
-
locals.keys,
|
287
|
-
).first
|
288
|
-
end
|
289
|
-
|
290
|
-
def prefixes_after(template_name)
|
291
|
-
prefixes = @lookup_override.prefixes
|
292
|
-
current_prefix = current_virtual_path.delete_suffix("/_#{template_name}")
|
293
|
-
|
294
|
-
if prefixes.include?(current_prefix)
|
295
|
-
prefixes.from(prefixes.index(current_prefix).to_i + 1)
|
296
|
-
else
|
297
|
-
prefixes
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
def about_to_recurse_infinitely?(partial)
|
302
|
-
partial.virtual_path == current_virtual_path
|
303
|
-
end
|
304
|
-
|
305
|
-
def current_virtual_path
|
306
|
-
if (current_template = @template.instance_values["current_template"])
|
307
|
-
current_template.virtual_path.to_s
|
308
|
-
else
|
309
|
-
@template.instance_values["virtual_path"].to_s
|
310
|
-
end
|
9
|
+
@default = dup
|
10
|
+
@template = TemplateProxy.new(builder: self, template: @template)
|
311
11
|
end
|
312
12
|
end
|
13
|
+
|
14
|
+
ActiveSupport.run_load_hooks(:view_partial_form_builder, FormBuilder)
|
313
15
|
end
|
@@ -11,28 +11,24 @@ module ViewPartialFormBuilder
|
|
11
11
|
|
12
12
|
prefixes = [
|
13
13
|
"#{object_name}/#{view_partial_directory}",
|
14
|
-
object_name,
|
15
|
-
view_partial_directory,
|
16
14
|
"#{root_prefix}/#{view_partial_directory}",
|
17
|
-
root_prefix,
|
18
15
|
]
|
19
16
|
|
20
17
|
overridden_prefixes.reverse_each do |prefix|
|
21
|
-
namespace, *files = prefix.split("/")
|
22
|
-
|
23
|
-
prefixes.unshift(prefix)
|
24
|
-
|
25
|
-
if namespace.present?
|
26
|
-
prefixes.unshift("#{namespace}/#{view_partial_directory}")
|
27
|
-
end
|
28
|
-
|
29
18
|
prefixes.unshift("#{prefix}/#{view_partial_directory}")
|
30
19
|
end
|
31
20
|
|
32
|
-
|
33
21
|
prefixes.uniq
|
34
22
|
end
|
35
23
|
|
24
|
+
def prefixes_after(current_prefix)
|
25
|
+
if prefixes.include?(current_prefix)
|
26
|
+
prefixes.from(prefixes.index(current_prefix).to_i + 1)
|
27
|
+
else
|
28
|
+
prefixes
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
36
32
|
private
|
37
33
|
|
38
34
|
attr_reader :object_name, :view_partial_directory
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module ViewPartialFormBuilder
|
2
|
+
class TemplateProxy
|
3
|
+
delegate :field_id, :field_name, to: :@template
|
4
|
+
|
5
|
+
def initialize(builder:, template:)
|
6
|
+
@template = template
|
7
|
+
@builder = builder
|
8
|
+
end
|
9
|
+
|
10
|
+
def button_tag(value, options, &block)
|
11
|
+
render(:button, arguments: [value, options], block: block) do
|
12
|
+
@template.button_tag(value, options, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def submit_tag(value, options)
|
17
|
+
render(:submit, arguments: [value, options]) do
|
18
|
+
@template.submit_tag(value, options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def label(object_name, method, content_or_options = nil, options = nil, &block)
|
23
|
+
if content_or_options.is_a?(Hash)
|
24
|
+
options.merge! content_or_options
|
25
|
+
content = nil
|
26
|
+
else
|
27
|
+
content = content_or_options
|
28
|
+
end
|
29
|
+
|
30
|
+
render(:label, arguments: [method, content, options], block: block) do
|
31
|
+
@template.label(object_name, method, content, options, &block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def method_missing(name, *arguments, &block)
|
38
|
+
if @builder.respond_to?(name)
|
39
|
+
arguments_after_object_name = arguments.from(1)
|
40
|
+
|
41
|
+
render(name, arguments: arguments_after_object_name, block: block) do
|
42
|
+
@template.public_send(name, *arguments, &block)
|
43
|
+
end
|
44
|
+
elsif @template.respond_to?(name)
|
45
|
+
@template.public_send(name, *arguments, &block)
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def respond_to_missing?(name, include_private = false)
|
52
|
+
@builder.respond_to_missing?(name) || @template.respond_to_missing?(name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def render(partial_name, arguments:, block: nil, &fallback)
|
56
|
+
locals = extract_partial_locals(partial_name, *arguments).merge(
|
57
|
+
form: @builder,
|
58
|
+
block: block
|
59
|
+
)
|
60
|
+
|
61
|
+
partial = find_partial(partial_name, locals)
|
62
|
+
|
63
|
+
if partial.nil? || about_to_recurse_infinitely?(partial)
|
64
|
+
fallback.call
|
65
|
+
else
|
66
|
+
partial.render(@template, locals, &block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def extract_partial_locals(form_method, *arguments)
|
71
|
+
parameters = @builder.method(form_method).parameters
|
72
|
+
|
73
|
+
parameters.each_with_index.each_with_object({}) { |(tuple, index), locals|
|
74
|
+
_type, parameter = tuple
|
75
|
+
|
76
|
+
locals[parameter] = arguments[index]
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def find_partial(template_name, locals)
|
81
|
+
current_prefix = current_virtual_path.delete_suffix("/_#{template_name}")
|
82
|
+
template_is_partial = true
|
83
|
+
|
84
|
+
partials = template_names_for(template_name).lazy.map do |name|
|
85
|
+
@template.lookup_context.find_all(
|
86
|
+
name,
|
87
|
+
lookup_override.prefixes_after(current_prefix),
|
88
|
+
template_is_partial,
|
89
|
+
locals.keys
|
90
|
+
).first
|
91
|
+
end
|
92
|
+
|
93
|
+
partials.find(&:present?)
|
94
|
+
end
|
95
|
+
|
96
|
+
def template_names_for(template_name)
|
97
|
+
[template_name, *@builder.aliased_field_helpers[template_name]].compact
|
98
|
+
end
|
99
|
+
|
100
|
+
def about_to_recurse_infinitely?(partial)
|
101
|
+
partial.virtual_path == current_virtual_path
|
102
|
+
end
|
103
|
+
|
104
|
+
def current_virtual_path
|
105
|
+
if (current_template = @template.instance_values["current_template"])
|
106
|
+
current_template.virtual_path.to_s
|
107
|
+
else
|
108
|
+
@template.instance_values["virtual_path"].to_s
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def lookup_override
|
113
|
+
LookupOverride.new(
|
114
|
+
prefixes: @template.lookup_context.prefixes,
|
115
|
+
object_name: @builder.object.try(:model_name) || @builder.object_name,
|
116
|
+
view_partial_directory: ViewPartialFormBuilder.view_partial_directory
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_partial_form_builder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Doyle
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionview
|
@@ -16,36 +16,22 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: railties
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 4.0.0
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 4.0.0
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: minitest-around
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
44
30
|
requirements:
|
45
31
|
- - ">="
|
46
32
|
- !ruby/object:Gem::Version
|
47
33
|
version: '0'
|
48
|
-
type: :
|
34
|
+
type: :runtime
|
49
35
|
prerelease: false
|
50
36
|
version_requirements: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
@@ -53,33 +39,19 @@ dependencies:
|
|
53
39
|
- !ruby/object:Gem::Version
|
54
40
|
version: '0'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
42
|
+
name: zeitwerk
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
45
|
- - ">="
|
60
46
|
- !ruby/object:Gem::Version
|
61
|
-
version: 4.0
|
62
|
-
type: :
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 4.0.0
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: activerecord-nulldb-adapter
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
47
|
+
version: 2.4.0
|
48
|
+
type: :runtime
|
77
49
|
prerelease: false
|
78
50
|
version_requirements: !ruby/object:Gem::Requirement
|
79
51
|
requirements:
|
80
52
|
- - ">="
|
81
53
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
54
|
+
version: 2.4.0
|
83
55
|
description: A Rails form builder where all designer-facing configuration is via templates.
|
84
56
|
email:
|
85
57
|
- sean.p.doyle24@gmail.com
|
@@ -93,14 +65,14 @@ files:
|
|
93
65
|
- lib/view_partial_form_builder.rb
|
94
66
|
- lib/view_partial_form_builder/engine.rb
|
95
67
|
- lib/view_partial_form_builder/form_builder.rb
|
96
|
-
- lib/view_partial_form_builder/html_attributes.rb
|
97
68
|
- lib/view_partial_form_builder/lookup_override.rb
|
69
|
+
- lib/view_partial_form_builder/template_proxy.rb
|
98
70
|
- lib/view_partial_form_builder/version.rb
|
99
71
|
homepage: https://github.com/seanpdoyle/view_partial_form_builder
|
100
72
|
licenses:
|
101
73
|
- MIT
|
102
74
|
metadata: {}
|
103
|
-
post_install_message:
|
75
|
+
post_install_message:
|
104
76
|
rdoc_options: []
|
105
77
|
require_paths:
|
106
78
|
- lib
|
@@ -115,8 +87,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
87
|
- !ruby/object:Gem::Version
|
116
88
|
version: '0'
|
117
89
|
requirements: []
|
118
|
-
rubygems_version: 3.
|
119
|
-
signing_key:
|
90
|
+
rubygems_version: 3.5.16
|
91
|
+
signing_key:
|
120
92
|
specification_version: 4
|
121
93
|
summary: Construct <form> element fields by combining ActionView::Helpers::FormBuilder
|
122
94
|
with Rails View Partials
|
@@ -1,47 +0,0 @@
|
|
1
|
-
module ViewPartialFormBuilder
|
2
|
-
class HtmlAttributes
|
3
|
-
def initialize(**attributes)
|
4
|
-
@attributes = attributes
|
5
|
-
end
|
6
|
-
|
7
|
-
def merge_token_lists(**token_list_attributes)
|
8
|
-
merge(
|
9
|
-
token_list_attributes.reduce({}) do |merged, (key, value)|
|
10
|
-
token_list = Array(attributes.delete(key)).unshift(value)
|
11
|
-
|
12
|
-
merged.merge(key => token_list.flatten.uniq)
|
13
|
-
end
|
14
|
-
)
|
15
|
-
end
|
16
|
-
|
17
|
-
def to_h
|
18
|
-
attributes.to_h
|
19
|
-
end
|
20
|
-
|
21
|
-
def to_hash
|
22
|
-
attributes.to_hash
|
23
|
-
end
|
24
|
-
|
25
|
-
def method_missing(method_name, *arguments, &block)
|
26
|
-
if attributes.respond_to?(method_name)
|
27
|
-
return_value = attributes.public_send(method_name, *arguments, &block)
|
28
|
-
|
29
|
-
if return_value.kind_of?(Hash)
|
30
|
-
HtmlAttributes.new(return_value)
|
31
|
-
else
|
32
|
-
return_value
|
33
|
-
end
|
34
|
-
else
|
35
|
-
super
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def respond_to_missing?(method_name, include_private = false)
|
40
|
-
attributes.respond_to?(method_name) || super
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
attr_reader :attributes
|
46
|
-
end
|
47
|
-
end
|