view_partial_form_builder 0.1.0 → 0.1.5
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 +120 -37
- data/lib/view_partial_form_builder/form_builder.rb +81 -30
- data/lib/view_partial_form_builder/html_attributes.rb +8 -0
- data/lib/view_partial_form_builder/{lookup_context.rb → lookup_override.rb} +9 -22
- data/lib/view_partial_form_builder/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d1550e43fb981483d25dedf89ab64478b5dd2c5803b4aa7345ae6d7efba4fe2
|
4
|
+
data.tar.gz: d8bb992d8d959d2288c761d764275af9e470081145884b6bfcff7b7dda3fdc45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 121978b8280e71e3cf114291cf4874949c9323f7b909ab7cd93602f1a3fb00b143c2028ed1188623c8b926efe8b3a27e67005947599d66b8e1cb33c98d8c589e
|
7
|
+
data.tar.gz: 9e56d40123f55fac7ee53b3bd3e087ccc35bdf3ca709485cad0d636fb41f7f684744dbc068d7af13bcdc6aa246e9707373c557f792d86a1d3fee1cd76468e4f0
|
data/README.md
CHANGED
@@ -87,7 +87,7 @@ generating HTML through Rails' helpers:
|
|
87
87
|
<%# app/views/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,31 +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
|
-
* `arguments` - an Array containing the arguments the helper received, in the
|
120
|
-
order they were received. This can be useful to pass to the view partial's
|
121
|
-
helper by [splatting them][splat] out.
|
122
|
-
|
123
119
|
* `&block` - a callable, `yield`-able block if the helper method was passed one
|
124
120
|
|
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
|
-
```
|
143
|
-
|
144
121
|
#### Handling DOMTokenList attributes
|
145
122
|
|
146
123
|
An [HTML element's `class` attribute][mdn-class] is treated by browsers as a
|
@@ -158,16 +135,12 @@ When rendering a field's DOMTokenList-backed attributes (like `class` or
|
|
158
135
|
controllers][stimulus-controller]), transforming and combining singular `String`
|
159
136
|
instances into lists of token can be very useful.
|
160
137
|
|
161
|
-
To simplify those scenarios, a partial's template-local optional attributes are
|
162
|
-
made available with the `#merge_token_lists` method.
|
163
|
-
|
164
138
|
These optional attributes are available through the `options` or `html_options`
|
165
139
|
partial-local variables. Their name will depend on the partial's corresponding
|
166
140
|
[`ActionView::Helpers::FormBuilder`][FormBuilder] interface.
|
167
141
|
|
168
|
-
|
169
|
-
|
170
|
-
Given call to `form.text_field` and a corresponding partial declaration:
|
142
|
+
To "merge" attributes together, you can combine Ruby's `String` interpolation
|
143
|
+
and `Hash#delete`:
|
171
144
|
|
172
145
|
```html+erb
|
173
146
|
<%# app/views/users/new.html.erb %>
|
@@ -177,7 +150,11 @@ Given call to `form.text_field` and a corresponding partial declaration:
|
|
177
150
|
|
178
151
|
<# app/views/form_builder/_text_field.html.erb %>
|
179
152
|
|
180
|
-
<%= form.text_field(
|
153
|
+
<%= form.text_field(
|
154
|
+
method,
|
155
|
+
class: "text-field #{options.delete(:class)}",
|
156
|
+
**options
|
157
|
+
) %>
|
181
158
|
```
|
182
159
|
|
183
160
|
The resulting HTML `<input>` element will merge have its [`class`
|
@@ -192,7 +169,6 @@ values:
|
|
192
169
|
[button]: https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-button
|
193
170
|
[local_assigns]: https://api.rubyonrails.org/classes/ActionView/Template.html#method-i-local_assigns
|
194
171
|
[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
172
|
[mdn-class]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
|
197
173
|
[DOMTokenList]: https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList
|
198
174
|
[classList]: https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
|
@@ -210,7 +186,7 @@ block-local `form` variable:
|
|
210
186
|
```erb
|
211
187
|
<%# app/views/users/form_builder/_email_field.html.erb %>
|
212
188
|
|
213
|
-
<%= form.default.email_field(
|
189
|
+
<%= form.default.email_field(method, options) %>
|
214
190
|
```
|
215
191
|
|
216
192
|
When passing a `model:` or `scope:` to calls to [`form_with`][form_with],
|
@@ -225,21 +201,128 @@ For example, when calling `form_with(model: User.new)`, a partial declared in
|
|
225
201
|
<%# app/views/users/form_builder/_password_field.html.erb %>
|
226
202
|
|
227
203
|
<div class="password-field-wrapper">
|
228
|
-
<%= form.password_field(
|
204
|
+
<%= form.password_field(method, options) %>
|
229
205
|
</div>
|
230
206
|
```
|
231
207
|
|
232
|
-
If you'd like to render a specific partial for a field,
|
233
|
-
|
208
|
+
If you'd like to render a specific partial for a field, make sure that you pass
|
209
|
+
along the `form:` (along with any other partial-local variables) as part of the
|
210
|
+
`render` call's `locals:` option:
|
211
|
+
|
234
212
|
|
235
213
|
```erb
|
236
214
|
<%# app/views/users/new.html.erb %>
|
237
215
|
|
238
216
|
<%= form_with(model: User.new) do |form| %>
|
239
|
-
<%=
|
217
|
+
<%= render("emails/my_special_email_field", {
|
218
|
+
form: form,
|
219
|
+
method: :email,
|
220
|
+
options: { class: "user-email" },
|
221
|
+
) %>
|
240
222
|
<% end %>
|
223
|
+
|
224
|
+
<%# app/views/emails/_my_special_email_field.html.erb %>
|
225
|
+
|
226
|
+
<%= form.email_field(
|
227
|
+
method,
|
228
|
+
class: "my-special-email #{options.delete(:class)},
|
229
|
+
**options
|
230
|
+
) %>
|
241
231
|
```
|
242
232
|
|
233
|
+
#### Composing partials
|
234
|
+
|
235
|
+
Layering partials on top of one another can be useful to share foundational
|
236
|
+
styles and configuration across your fields. For instance, consider an
|
237
|
+
administrative interface that shares styles with a consumer facing site, but has
|
238
|
+
additional bells and whistles.
|
239
|
+
|
240
|
+
Declare the consumer facing inputs (in this example, `<input type="search">`):
|
241
|
+
|
242
|
+
```html+erb
|
243
|
+
<%# app/views/form_builder/_search_field.html.erb %>
|
244
|
+
|
245
|
+
<%= form.search_field(
|
246
|
+
method,
|
247
|
+
class: "
|
248
|
+
search-field
|
249
|
+
#{options.delete(:class}
|
250
|
+
",
|
251
|
+
"data-controller": "
|
252
|
+
input->search#executeQuery
|
253
|
+
#{options.delete(:"data-controller")}
|
254
|
+
",
|
255
|
+
**options
|
256
|
+
) %>
|
257
|
+
```
|
258
|
+
|
259
|
+
Then, declare the administrative interface's inputs, in terms of overriding the
|
260
|
+
foundation built by the more general definitions:
|
261
|
+
|
262
|
+
```html+erb
|
263
|
+
<%# app/views/admin/form_builder/_search_field.html.erb %>
|
264
|
+
|
265
|
+
<%= form.search_field(
|
266
|
+
method,
|
267
|
+
class: "
|
268
|
+
search-field--admin
|
269
|
+
#{options.delete(:class}
|
270
|
+
",
|
271
|
+
"data-controller": "
|
272
|
+
focus->admin-search#clearResults
|
273
|
+
#{options.delete(:"data-controller")}
|
274
|
+
",
|
275
|
+
) %>
|
276
|
+
```
|
277
|
+
|
278
|
+
The rendered `admin/form_builder/search_field` partial combines options and
|
279
|
+
arguments from both partials:
|
280
|
+
|
281
|
+
```html
|
282
|
+
<input
|
283
|
+
type="search"
|
284
|
+
class="
|
285
|
+
search-field
|
286
|
+
search-field--admin
|
287
|
+
"
|
288
|
+
data-controller="
|
289
|
+
input->search#executeQuery
|
290
|
+
focus->admin-search#clearResults
|
291
|
+
"
|
292
|
+
>
|
293
|
+
```
|
294
|
+
|
295
|
+
When constructing fields within a `form_with(model: ...)` block, partials will
|
296
|
+
use the `model:` instance's [`tableize`-d model name][tableize] to resolve
|
297
|
+
partials.
|
298
|
+
|
299
|
+
For example, `posts/form_builder/_text_field.html.erb` will be resolved ahead of
|
300
|
+
`form_builder/_text_field.html.erb`:
|
301
|
+
|
302
|
+
```html+erb
|
303
|
+
<%# app/views/posts/form_builder/_text_field.html.erb %>
|
304
|
+
|
305
|
+
<%= form.text_field(method, class: "post-text #{options.delete(:class)}", **options) %>
|
306
|
+
|
307
|
+
<%# app/views/application/form_builder/_text_field.html.erb %>
|
308
|
+
|
309
|
+
<%= form.text_field(method, class: "text #{options.delete(:class)}", **options) %>
|
310
|
+
```
|
311
|
+
|
312
|
+
The rendered `posts/form_builder/text_field` partial could combine options and
|
313
|
+
arguments from both partials:
|
314
|
+
|
315
|
+
```html
|
316
|
+
<input type="text" class="post-text text">
|
317
|
+
```
|
318
|
+
|
319
|
+
Models declared within modules will be delimited with `/`. For example,
|
320
|
+
`Special::Post` instances would first resolve partials within the
|
321
|
+
`app/views/special/posts/form_builder` directory, before falling back to
|
322
|
+
`app/views/application/form_builder`.
|
323
|
+
|
324
|
+
[tableize]: https://api.rubyonrails.org/classes/String.html#method-i-tableize
|
325
|
+
|
243
326
|
### Configuration
|
244
327
|
|
245
328
|
View partials lookup and resolution will be scoped to the
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require "view_partial_form_builder/
|
1
|
+
require "view_partial_form_builder/lookup_override"
|
2
2
|
require "view_partial_form_builder/html_attributes"
|
3
3
|
|
4
4
|
module ViewPartialFormBuilder
|
@@ -14,9 +14,9 @@ module ViewPartialFormBuilder
|
|
14
14
|
@template,
|
15
15
|
options,
|
16
16
|
)
|
17
|
-
@
|
18
|
-
|
19
|
-
object_name: object_name,
|
17
|
+
@lookup_override = LookupOverride.new(
|
18
|
+
prefixes: @template.lookup_context.prefixes,
|
19
|
+
object_name: object&.model_name || object_name,
|
20
20
|
view_partial_directory: ViewPartialFormBuilder.view_partial_directory,
|
21
21
|
)
|
22
22
|
end
|
@@ -173,6 +173,17 @@ module ViewPartialFormBuilder
|
|
173
173
|
render_partial("time_zone_select", locals, fallback: -> { super })
|
174
174
|
end
|
175
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
|
+
|
176
187
|
def hidden_field(method, **options)
|
177
188
|
@emitted_hidden_id = true if method == :id
|
178
189
|
|
@@ -247,50 +258,90 @@ module ViewPartialFormBuilder
|
|
247
258
|
|
248
259
|
private
|
249
260
|
|
250
|
-
attr_reader :lookup_context
|
251
|
-
|
252
261
|
def render_partial(field, locals, fallback:, &block)
|
253
|
-
return fallback.call if about_to_recurse_infinitely?(field)
|
254
|
-
|
255
262
|
options = locals.fetch(:options, {})
|
256
263
|
partial_override = options.delete(:partial)
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
264
|
+
options_as_locals = objectify_options(options)
|
265
|
+
locals = DeprecatedHash.new(
|
266
|
+
options_as_locals.merge(locals, form: self),
|
267
|
+
deprecated_keys: options_as_locals.keys + [:arguments],
|
268
|
+
)
|
269
|
+
|
270
|
+
partial = if partial_override.present?
|
271
|
+
ActiveSupport::Deprecation.new("0.2.0", "ViewPartialFormBuilder").warn(<<~WARNING)
|
272
|
+
Providing a `partial:` option for a form field is deprecated.
|
273
|
+
WARNING
|
274
|
+
|
275
|
+
find_partial(partial_override, locals, prefixes: [])
|
276
|
+
else
|
277
|
+
find_partial(field, locals, prefixes: prefixes_after(field))
|
278
|
+
end
|
279
|
+
|
280
|
+
if partial.nil? || about_to_recurse_infinitely?(partial)
|
281
|
+
fallback.call
|
282
|
+
else
|
283
|
+
partial.render(@template, locals, &block)
|
267
284
|
end
|
268
285
|
end
|
269
286
|
|
270
|
-
def
|
287
|
+
def find_partial(template_name, locals, prefixes:)
|
271
288
|
template_is_partial = true
|
272
289
|
|
273
|
-
lookup_context.
|
290
|
+
@template.lookup_context.find_all(
|
274
291
|
template_name,
|
275
|
-
|
292
|
+
prefixes,
|
276
293
|
template_is_partial,
|
277
|
-
|
294
|
+
locals.keys,
|
295
|
+
).first.tap do |partial|
|
296
|
+
root_directory = ViewPartialFormBuilder.view_partial_directory
|
297
|
+
|
298
|
+
if partial&.virtual_path == "#{root_directory}/_#{template_name}"
|
299
|
+
ActiveSupport::Deprecation.new("0.2.0", "ViewPartialFormBuilder").warn(<<~WARNING.strip)
|
300
|
+
Declare root-level partials in app/views/application/#{root_directory}/, not app/views/#{root_directory}/.
|
301
|
+
WARNING
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def prefixes_after(template_name)
|
307
|
+
prefixes = @lookup_override.prefixes
|
308
|
+
current_prefix = current_virtual_path.delete_suffix("/_#{template_name}")
|
309
|
+
|
310
|
+
if prefixes.include?(current_prefix)
|
311
|
+
prefixes.from(prefixes.index(current_prefix).to_i + 1)
|
312
|
+
else
|
313
|
+
prefixes
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def about_to_recurse_infinitely?(partial)
|
318
|
+
partial.virtual_path == current_virtual_path
|
278
319
|
end
|
279
320
|
|
280
|
-
def
|
281
|
-
if
|
282
|
-
|
321
|
+
def current_virtual_path
|
322
|
+
if (current_template = @template.instance_values["current_template"])
|
323
|
+
current_template.virtual_path.to_s
|
283
324
|
else
|
284
|
-
@template.
|
325
|
+
@template.instance_values["virtual_path"].to_s
|
285
326
|
end
|
286
327
|
end
|
328
|
+
end
|
287
329
|
|
288
|
-
|
289
|
-
|
290
|
-
|
330
|
+
class DeprecatedHash < Hash
|
331
|
+
def initialize(hash, deprecated_keys: [])
|
332
|
+
super()
|
333
|
+
merge!(hash)
|
334
|
+
@deprecated_keys = deprecated_keys
|
335
|
+
end
|
291
336
|
|
292
|
-
|
337
|
+
def [](key)
|
338
|
+
if @deprecated_keys.include?(key)
|
339
|
+
ActiveSupport::Deprecation.new("0.2.0", "ViewPartialFormBuilder").warn <<~WARNING
|
340
|
+
Accessing `#{key}` from partials is deprecated.
|
341
|
+
WARNING
|
293
342
|
end
|
343
|
+
|
344
|
+
super
|
294
345
|
end
|
295
346
|
end
|
296
347
|
end
|
@@ -1,5 +1,13 @@
|
|
1
1
|
module ViewPartialFormBuilder
|
2
2
|
class HtmlAttributes
|
3
|
+
deprecate merge_token_lists: <<~'WARNING', deprecator: ActiveSupport::Deprecation.new("0.2.0", "ViewPartialFormBuilder")
|
4
|
+
|
5
|
+
As an alternative, merge arguments through String interpolation:
|
6
|
+
|
7
|
+
<%= form.label(method, class: "label #{options.delete(:class)}", **options) %>
|
8
|
+
|
9
|
+
WARNING
|
10
|
+
|
3
11
|
def initialize(**attributes)
|
4
12
|
@attributes = attributes
|
5
13
|
end
|
@@ -1,29 +1,12 @@
|
|
1
1
|
module ViewPartialFormBuilder
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
@object_name = object_name.to_s.pluralize
|
7
|
-
@overridden_context = overridden_context
|
8
|
-
@prefixes = overridden_context.prefixes.dup.freeze
|
2
|
+
class LookupOverride
|
3
|
+
def initialize(prefixes:, object_name:, view_partial_directory:)
|
4
|
+
@object_name = object_name.to_s.tableize
|
5
|
+
@prefixes = prefixes
|
9
6
|
@view_partial_directory = view_partial_directory
|
10
7
|
end
|
11
8
|
|
12
|
-
def
|
13
|
-
previous_prefixes = overridden_context.prefixes
|
14
|
-
|
15
|
-
overridden_context.prefixes = prefix_overrides
|
16
|
-
|
17
|
-
yield
|
18
|
-
ensure
|
19
|
-
overridden_context.prefixes = previous_prefixes
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
attr_reader :overridden_context, :object_name, :view_partial_directory
|
25
|
-
|
26
|
-
def prefix_overrides
|
9
|
+
def prefixes
|
27
10
|
*overridden_prefixes, root_prefix = @prefixes.dup
|
28
11
|
|
29
12
|
prefixes = [
|
@@ -49,5 +32,9 @@ module ViewPartialFormBuilder
|
|
49
32
|
|
50
33
|
prefixes.uniq
|
51
34
|
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :object_name, :view_partial_directory
|
52
39
|
end
|
53
40
|
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.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Doyle
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionview
|
@@ -94,7 +94,7 @@ files:
|
|
94
94
|
- lib/view_partial_form_builder/engine.rb
|
95
95
|
- lib/view_partial_form_builder/form_builder.rb
|
96
96
|
- lib/view_partial_form_builder/html_attributes.rb
|
97
|
-
- lib/view_partial_form_builder/
|
97
|
+
- lib/view_partial_form_builder/lookup_override.rb
|
98
98
|
- lib/view_partial_form_builder/version.rb
|
99
99
|
homepage: https://github.com/seanpdoyle/view_partial_form_builder
|
100
100
|
licenses:
|